diff --git a/.changeset/react-router-v8.md b/.changeset/react-router-v8.md new file mode 100644 index 000000000..2c774129d --- /dev/null +++ b/.changeset/react-router-v8.md @@ -0,0 +1,61 @@ +--- +"@checkstack/about-frontend": minor +"@checkstack/ai-frontend": minor +"@checkstack/announcement-frontend": minor +"@checkstack/anomaly-frontend": minor +"@checkstack/api-docs-frontend": minor +"@checkstack/auth-frontend": minor +"@checkstack/automation-frontend": minor +"@checkstack/catalog-frontend": minor +"@checkstack/command-frontend": minor +"@checkstack/dashboard-frontend": minor +"@checkstack/dependency-frontend": minor +"@checkstack/frontend": minor +"@checkstack/gitops-frontend": minor +"@checkstack/healthcheck-frontend": minor +"@checkstack/incident-frontend": minor +"@checkstack/infrastructure-frontend": minor +"@checkstack/integration-frontend": minor +"@checkstack/logstream-frontend": minor +"@checkstack/maintenance-frontend": minor +"@checkstack/metricstream-frontend": minor +"@checkstack/notification-frontend": minor +"@checkstack/pluginmanager-frontend": minor +"@checkstack/queue-frontend": minor +"@checkstack/satellite-frontend": minor +"@checkstack/script-packages-frontend": minor +"@checkstack/secrets-frontend": minor +"@checkstack/slo-frontend": minor +"@checkstack/status-page-frontend": minor +"@checkstack/telemetry-frontend": minor +"@checkstack/tracestream-frontend": minor +"@checkstack/ui": minor +"@checkstack/ai-backend": patch +--- + +Migrate the frontend from react-router-dom v7 to react-router v8 + +Resolves GHSA-qwww-vcr4-c8h2 (HIGH): React Router before 8.3.0 has an RSC-mode +CSRF bypass that lets an action execute before the 400 response. Checkstack runs +a client-side SPA (``) and does not use RSC mode, so the platform +was not exploitable through it - but the advisory kept the dependency-graph +security gate red on every pull request, and the fix is only available in the 8.x +line, which the auto-remediation deliberately will not reach (it refuses major +bumps). + +`react-router-dom` has no v8: it was folded into `react-router` in v7 and v8 +ships as `react-router` only. So this is a package swap rather than a range bump: + +- 31 packages now depend on `react-router@^8.3.0` instead of + `react-router-dom@^7.16.0`, and 97 source files import from `react-router`. +- The Module Federation host share, `optimizeDeps` and `dedupe` entries move to + `react-router` (shared singleton `requiredVersion` `^8.0.0`). Remotes never + shared the router, so the remote contract is unchanged. +- The syncpack unified-range group tracks `react-router`, keeping the enforced + single-range guarantee that a past four-range regression motivated. + +The API surface Checkstack uses is unchanged between v7 and v8 - `BrowserRouter`, +`MemoryRouter`, `Routes`, `Route`, `Link`, `NavLink`, `useLocation`, +`useNavigate`, `useParams` and `useSearchParams` are all exported by v8 with the +same signatures - so no routing code changed beyond the import specifier. v8 +requires React >= 19.2.7, which the workspace already pins. diff --git a/.syncpackrc.json b/.syncpackrc.json index ab721f372..af2432a81 100644 --- a/.syncpackrc.json +++ b/.syncpackrc.json @@ -12,7 +12,7 @@ "dependencies": [ "react", "react-dom", - "react-router-dom", + "react-router", "@orpc/contract", "@orpc/server", "@orpc/client", diff --git a/bun.lock b/bun.lock index ddecc4be0..d05f8a95c 100644 --- a/bun.lock +++ b/bun.lock @@ -49,7 +49,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -115,7 +115,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -181,7 +181,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1", }, "devDependencies": { @@ -265,7 +265,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1", }, "devDependencies": { @@ -297,7 +297,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -365,7 +365,7 @@ "better-auth": "^1.6.13", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -459,7 +459,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "yaml": "^2.6.1", }, "devDependencies": { @@ -699,7 +699,7 @@ "@dnd-kit/utilities": "^3.2.2", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1", }, "devDependencies": { @@ -751,7 +751,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -813,7 +813,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -892,7 +892,7 @@ "@xyflow/react": "^12.11.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -986,7 +986,7 @@ "lucide-react": "^1.17.0", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7", @@ -1078,7 +1078,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1199,7 +1199,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "uuid": "^14.0.0", "zod": "^4.2.1", }, @@ -1286,7 +1286,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1320,7 +1320,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1397,7 +1397,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1489,7 +1489,7 @@ "@tanstack/react-query": "^5.100.14", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1572,7 +1572,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1653,7 +1653,7 @@ "@tanstack/react-query": "^5.100.14", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1720,7 +1720,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1763,7 +1763,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -1832,7 +1832,7 @@ "ajv-formats": "^3.0.1", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.0.0", }, "devDependencies": { @@ -1940,7 +1940,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -2023,7 +2023,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -2093,7 +2093,7 @@ }, "core/sdk": { "name": "@checkstack/sdk", - "version": "0.135.1", + "version": "0.136.0", "dependencies": { "@checkstack/announcement-common": "workspace:*", "@checkstack/anomaly-common": "workspace:*", @@ -2227,7 +2227,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -2354,7 +2354,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "recharts": "^3.8.1", }, "devDependencies": { @@ -2420,7 +2420,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/scripts": "workspace:*", @@ -2492,7 +2492,7 @@ "lucide-react": "^0.469.0", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1", }, "devDependencies": { @@ -2738,7 +2738,7 @@ "lucide-react": "^0.469.0", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", }, "devDependencies": { "@checkstack/dev-server": "workspace:*", @@ -2797,7 +2797,7 @@ "react-day-picker": "^9.13.0", "react-dom": "19.2.7", "react-markdown": "^10.1.0", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", @@ -5381,7 +5381,7 @@ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], - "cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], "core-js-compat": ["core-js-compat@3.49.0", "", { "dependencies": { "browserslist": "^4.28.1" } }, "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA=="], @@ -6427,9 +6427,7 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], - "react-router": ["react-router@7.18.1", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg=="], - - "react-router-dom": ["react-router-dom@7.18.1", "", { "dependencies": { "react-router": "7.18.1" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg=="], + "react-router": ["react-router@8.3.0", "", { "dependencies": { "cookie-es": "^3.1.1" }, "peerDependencies": { "react": ">=19.2.7", "react-dom": ">=19.2.7" }, "optionalPeers": ["react-dom"] }, "sha512-qyPMvW83jGIct3yiieisxdk9M745anqhpIMKN5m1t6yBMfgVPpt77aHOqs5fUlEJRMCGffg9BaQLH9oPVOL7xQ=="], "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], @@ -6577,7 +6575,7 @@ "semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], - "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "set-cookie-parser": ["set-cookie-parser@3.1.2", "", {}, "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw=="], "sharp": ["sharp@0.35.3", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.3", "@img/sharp-darwin-x64": "0.35.3", "@img/sharp-freebsd-wasm32": "0.35.3", "@img/sharp-libvips-darwin-arm64": "1.3.2", "@img/sharp-libvips-darwin-x64": "1.3.2", "@img/sharp-libvips-linux-arm": "1.3.2", "@img/sharp-libvips-linux-arm64": "1.3.2", "@img/sharp-libvips-linux-ppc64": "1.3.2", "@img/sharp-libvips-linux-riscv64": "1.3.2", "@img/sharp-libvips-linux-s390x": "1.3.2", "@img/sharp-libvips-linux-x64": "1.3.2", "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", "@img/sharp-libvips-linuxmusl-x64": "1.3.2", "@img/sharp-linux-arm": "0.35.3", "@img/sharp-linux-arm64": "0.35.3", "@img/sharp-linux-ppc64": "0.35.3", "@img/sharp-linux-riscv64": "0.35.3", "@img/sharp-linux-s390x": "0.35.3", "@img/sharp-linux-x64": "0.35.3", "@img/sharp-linuxmusl-arm64": "0.35.3", "@img/sharp-linuxmusl-x64": "0.35.3", "@img/sharp-webcontainers-wasm32": "0.35.3", "@img/sharp-win32-arm64": "0.35.3", "@img/sharp-win32-ia32": "0.35.3", "@img/sharp-win32-x64": "0.35.3" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q=="], @@ -7073,8 +7071,6 @@ "astro/package-manager-detector": ["package-manager-detector@1.7.0", "", {}, "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ=="], - "better-call/set-cookie-parser": ["set-cookie-parser@3.1.2", "", {}, "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw=="], - "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "buffer-image-size/@types/node": ["@types/node@24.13.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q=="], @@ -7125,6 +7121,8 @@ "globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "h3/cookie-es": ["cookie-es@1.2.3", "", {}, "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw=="], + "handlebars/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], diff --git a/core/about-frontend/package.json b/core/about-frontend/package.json index 963401632..a87397c0c 100644 --- a/core/about-frontend/package.json +++ b/core/about-frontend/package.json @@ -21,7 +21,7 @@ "@checkstack/common": "workspace:*", "@checkstack/ui": "workspace:*", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "lucide-react": "^1.17.0" }, "devDependencies": { diff --git a/core/about-frontend/src/AboutMenuItem.tsx b/core/about-frontend/src/AboutMenuItem.tsx index 96c28e471..a460005e4 100644 --- a/core/about-frontend/src/AboutMenuItem.tsx +++ b/core/about-frontend/src/AboutMenuItem.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { Info } from "lucide-react"; import { DropdownMenuItem } from "@checkstack/ui"; import { resolveRoute } from "@checkstack/common"; diff --git a/core/ai-backend/src/generated/docs-index.ts b/core/ai-backend/src/generated/docs-index.ts index 6afe7e9c6..5a6d6cba6 100644 --- a/core/ai-backend/src/generated/docs-index.ts +++ b/core/ai-backend/src/generated/docs-index.ts @@ -647,7 +647,7 @@ export const DOCS_INDEX: readonly DocsIndexEntry[] = [ "Frontend Flow", "Access Filtering" ], - "content": "The Command Palette provides a unified, keyboard-accessible interface for navigating the application and executing commands. Plugins can contribute both **commands** (actionable items with optional keyboard shortcuts) and **searchable entities** (like systems, incidents, etc.) to the palette.\n\n## Overview\n\nThe command palette system consists of three packages:\n\n| Package | Purpose |\n|---------|---------|\n| `@checkstack/command-backend` | Backend registry and search aggregation |\n| `@checkstack/command-common` | Shared types and RPC contract |\n| `@checkstack/command-frontend` | Frontend hooks for search and shortcuts |\n\n## Registering Commands\n\nCommands are actionable items that navigate users to specific routes. They can have keyboard shortcuts for quick access.\n\n### Simple Command Registration\n\nFor most plugins, use the simplified `commands` API:\n\n```typescript\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport { pluginMetadata, access } from \"@checkstack/my-plugin-common\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport { myRoutes } from \"./routes\";\n\n// In your plugin's init phase\nregisterSearchProvider({\n pluginMetadata,\n commands: [\n {\n id: \"create\",\n title: \"Create Item\",\n subtitle: \"Create a new item\",\n iconName: \"Plus\", // Lucide icon name\n shortcuts: [\"meta+shift+n\", \"ctrl+shift+n\"],\n route: resolveRoute(myRoutes.routes.config) + \"?action=create\",\n requiredAccessRules: [access.myAccess],\n },\n {\n id: \"manage\",\n title: \"Manage Items\",\n subtitle: \"View and manage all items\",\n iconName: \"List\",\n route: resolveRoute(myRoutes.routes.config),\n // No requiredAccessRules = visible to all users\n },\n ],\n});\n```\n\n### CommandDefinition Properties\n\n| Property | Type | Required | Description |\n|----------|------|----------|-------------|\n| `id` | `string` | ✅ | Unique ID (prefixed with plugin ID automatically) |\n| `title` | `string` | ✅ | Display title in the palette |\n| `subtitle` | `string` | ❌ | Secondary description text |\n| `iconName` | `string` | ❌ | Lucide icon name (e.g., \"AlertCircle\", \"Wrench\") |\n| `shortcuts` | `string[]` | ❌ | Keyboard shortcuts (e.g., `[\"meta+shift+i\", \"ctrl+shift+i\"]`) |\n| `route` | `string` | ✅ | Navigation route when command is executed |\n| `requiredAccessRules` | `AccessRule[]` | ❌ | AccessRule objects (auto-qualified) |\n\n### Keyboard Shortcuts\n\nShortcuts use a cross-platform format:\n- `meta` = ⌘ on Mac, Windows key on Windows\n- `ctrl` = Control key\n- `shift` = Shift key\n- `alt` = ⌥ on Mac, Alt on Windows\n\nFor cross-platform support, define both variants:\n```typescript\nshortcuts: [\"meta+shift+i\", \"ctrl+shift+i\"]\n```\n\n## Registering Searchable Entities\n\nFor searchable entities (like systems, incidents, etc.), use the `provider` option:\n\n```typescript\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport { pluginMetadata } from \"@checkstack/my-plugin-common\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport * as schema from \"./schema\";\n\nregisterSearchProvider({\n pluginMetadata,\n provider: {\n id: \"items\",\n name: \"Items\",\n priority: 100, // Higher = appears first (default: 0)\n search: async (query, { userAccessRules }) => {\n const db = getDatabase(); // Your database access\n const items = await db.select().from(schema.items);\n const q = query.toLowerCase();\n\n return items\n .filter(\n (item) =>\n !q ||\n item.name.toLowerCase().includes(q) ||\n item.description?.toLowerCase().includes(q)\n )\n .map((item) => ({\n id: item.id,\n type: \"entity\" as const,\n title: item.name,\n subtitle: item.description,\n category: \"Items\",\n iconName: \"Box\",\n route: resolveRoute(myRoutes.routes.detail, { itemId: item.id }),\n // Optional: requiredAccessRules for access filtering\n }));\n },\n },\n});\n```\n\n### SearchResult Properties\n\n| Property | Type | Required | Description |\n|----------|------|----------|-------------|\n| `id` | `string` | ✅ | Unique identifier |\n| `type` | `\"entity\" \\| \"command\"` | ✅ | Result type |\n| `title` | `string` | ✅ | Primary display text |\n| `subtitle` | `string` | ❌ | Secondary text |\n| `category` | `string` | ✅ | Grouping label (e.g., \"Systems\", \"Incidents\") |\n| `iconName` | `string` | ❌ | Lucide icon name |\n| `route` | `string` | ❌ | Navigation route |\n| `shortcuts` | `string[]` | ❌ | Keyboard shortcuts (commands only) |\n| `requiredAccessRules` | `string[]` | ❌ | Required access rule IDs |\n\n## Access Handling\n\nAccess rules are **automatically qualified** with the plugin ID:\n\n```typescript\n// You provide:\nrequiredAccessRules: [access.myManage]\n// which has id: \"my.manage\"\n\n// Becomes fully qualified:\n// \"my-plugin.my.manage\"\n```\n\nUsers with the `\"*\"` (wildcard) access see all commands regardless of requirements.\n\n## URL Action Parameters\n\nTo support deep-linking from commands (e.g., \"Create Incident\" → opens create dialog), handle URL parameters in your frontend:\n\n```tsx\nimport { useSearchParams } from \"react-router-dom\";\n\nfunction MyConfigPage() {\n const [searchParams, setSearchParams] = useSearchParams();\n const [editorOpen, setEditorOpen] = useState(false);\n\n // Handle ?action=create URL parameter\n useEffect(() => {\n if (searchParams.get(\"action\") === \"create\" && canManage) {\n setEditorOpen(true);\n // Clear the URL param after opening\n searchParams.delete(\"action\");\n setSearchParams(searchParams, { replace: true });\n }\n }, [searchParams, canManage, setSearchParams]);\n\n // ...\n}\n```\n\n## Complete Example: Incident Plugin\n\nHere's how the incident plugin registers its commands:\n\n```typescript\n// incident-backend/src/index.ts\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport {\n pluginMetadata,\n access,\n incidentRoutes,\n} from \"@checkstack/incident-common\";\nimport { resolveRoute } from \"@checkstack/common\";\n\nexport default createBackendPlugin({\n metadata: pluginMetadata,\n register(env) {\n env.registerInit({\n deps: { /* ... */ },\n init: async ({ /* ... */ }) => {\n // ... other initialization ...\n\n // Register command palette entries\n registerSearchProvider({\n pluginMetadata,\n commands: [\n {\n id: \"create\",\n title: \"Create Incident\",\n subtitle: \"Report a new incident affecting systems\",\n iconName: \"AlertCircle\",\n shortcuts: [\"meta+shift+i\", \"ctrl+shift+i\"],\n route: resolveRoute(incidentRoutes.routes.config) + \"?action=create\",\n requiredAccessRules: [access.incidentManage],\n },\n ],\n });\n },\n });\n },\n});\n```\n\n## Architecture Notes\n\n### Backend Flow\n\n1. Plugins call `registerSearchProvider()` during initialization\n2. The command backend aggregates all providers\n3. When a search query comes in:\n - All providers are queried in parallel\n - Results are flattened and sorted by priority\n - Results are filtered by user access rules\n - Filtered results are returned to the frontend\n\n### Frontend Flow\n\n1. `GlobalShortcuts` component fetches commands on mount\n2. Global keyboard listeners are registered for all shortcuts\n3. When the palette opens, `search(\"\")` fetches all available items\n4. As the user types, results are filtered by the query\n5. Selecting a result navigates to the specified route\n\n### Access Filtering\n\nResults are filtered twice:\n1. **Backend**: `filterByAccessRules()` removes results the user can't access\n2. **Frontend**: Global shortcuts also check access before triggering\n\nUsers with the `\"*\"` wildcard access bypass all access checks.", + "content": "The Command Palette provides a unified, keyboard-accessible interface for navigating the application and executing commands. Plugins can contribute both **commands** (actionable items with optional keyboard shortcuts) and **searchable entities** (like systems, incidents, etc.) to the palette.\n\n## Overview\n\nThe command palette system consists of three packages:\n\n| Package | Purpose |\n|---------|---------|\n| `@checkstack/command-backend` | Backend registry and search aggregation |\n| `@checkstack/command-common` | Shared types and RPC contract |\n| `@checkstack/command-frontend` | Frontend hooks for search and shortcuts |\n\n## Registering Commands\n\nCommands are actionable items that navigate users to specific routes. They can have keyboard shortcuts for quick access.\n\n### Simple Command Registration\n\nFor most plugins, use the simplified `commands` API:\n\n```typescript\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport { pluginMetadata, access } from \"@checkstack/my-plugin-common\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport { myRoutes } from \"./routes\";\n\n// In your plugin's init phase\nregisterSearchProvider({\n pluginMetadata,\n commands: [\n {\n id: \"create\",\n title: \"Create Item\",\n subtitle: \"Create a new item\",\n iconName: \"Plus\", // Lucide icon name\n shortcuts: [\"meta+shift+n\", \"ctrl+shift+n\"],\n route: resolveRoute(myRoutes.routes.config) + \"?action=create\",\n requiredAccessRules: [access.myAccess],\n },\n {\n id: \"manage\",\n title: \"Manage Items\",\n subtitle: \"View and manage all items\",\n iconName: \"List\",\n route: resolveRoute(myRoutes.routes.config),\n // No requiredAccessRules = visible to all users\n },\n ],\n});\n```\n\n### CommandDefinition Properties\n\n| Property | Type | Required | Description |\n|----------|------|----------|-------------|\n| `id` | `string` | ✅ | Unique ID (prefixed with plugin ID automatically) |\n| `title` | `string` | ✅ | Display title in the palette |\n| `subtitle` | `string` | ❌ | Secondary description text |\n| `iconName` | `string` | ❌ | Lucide icon name (e.g., \"AlertCircle\", \"Wrench\") |\n| `shortcuts` | `string[]` | ❌ | Keyboard shortcuts (e.g., `[\"meta+shift+i\", \"ctrl+shift+i\"]`) |\n| `route` | `string` | ✅ | Navigation route when command is executed |\n| `requiredAccessRules` | `AccessRule[]` | ❌ | AccessRule objects (auto-qualified) |\n\n### Keyboard Shortcuts\n\nShortcuts use a cross-platform format:\n- `meta` = ⌘ on Mac, Windows key on Windows\n- `ctrl` = Control key\n- `shift` = Shift key\n- `alt` = ⌥ on Mac, Alt on Windows\n\nFor cross-platform support, define both variants:\n```typescript\nshortcuts: [\"meta+shift+i\", \"ctrl+shift+i\"]\n```\n\n## Registering Searchable Entities\n\nFor searchable entities (like systems, incidents, etc.), use the `provider` option:\n\n```typescript\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport { pluginMetadata } from \"@checkstack/my-plugin-common\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport * as schema from \"./schema\";\n\nregisterSearchProvider({\n pluginMetadata,\n provider: {\n id: \"items\",\n name: \"Items\",\n priority: 100, // Higher = appears first (default: 0)\n search: async (query, { userAccessRules }) => {\n const db = getDatabase(); // Your database access\n const items = await db.select().from(schema.items);\n const q = query.toLowerCase();\n\n return items\n .filter(\n (item) =>\n !q ||\n item.name.toLowerCase().includes(q) ||\n item.description?.toLowerCase().includes(q)\n )\n .map((item) => ({\n id: item.id,\n type: \"entity\" as const,\n title: item.name,\n subtitle: item.description,\n category: \"Items\",\n iconName: \"Box\",\n route: resolveRoute(myRoutes.routes.detail, { itemId: item.id }),\n // Optional: requiredAccessRules for access filtering\n }));\n },\n },\n});\n```\n\n### SearchResult Properties\n\n| Property | Type | Required | Description |\n|----------|------|----------|-------------|\n| `id` | `string` | ✅ | Unique identifier |\n| `type` | `\"entity\" \\| \"command\"` | ✅ | Result type |\n| `title` | `string` | ✅ | Primary display text |\n| `subtitle` | `string` | ❌ | Secondary text |\n| `category` | `string` | ✅ | Grouping label (e.g., \"Systems\", \"Incidents\") |\n| `iconName` | `string` | ❌ | Lucide icon name |\n| `route` | `string` | ❌ | Navigation route |\n| `shortcuts` | `string[]` | ❌ | Keyboard shortcuts (commands only) |\n| `requiredAccessRules` | `string[]` | ❌ | Required access rule IDs |\n\n## Access Handling\n\nAccess rules are **automatically qualified** with the plugin ID:\n\n```typescript\n// You provide:\nrequiredAccessRules: [access.myManage]\n// which has id: \"my.manage\"\n\n// Becomes fully qualified:\n// \"my-plugin.my.manage\"\n```\n\nUsers with the `\"*\"` (wildcard) access see all commands regardless of requirements.\n\n## URL Action Parameters\n\nTo support deep-linking from commands (e.g., \"Create Incident\" → opens create dialog), handle URL parameters in your frontend:\n\n```tsx\nimport { useSearchParams } from \"react-router\";\n\nfunction MyConfigPage() {\n const [searchParams, setSearchParams] = useSearchParams();\n const [editorOpen, setEditorOpen] = useState(false);\n\n // Handle ?action=create URL parameter\n useEffect(() => {\n if (searchParams.get(\"action\") === \"create\" && canManage) {\n setEditorOpen(true);\n // Clear the URL param after opening\n searchParams.delete(\"action\");\n setSearchParams(searchParams, { replace: true });\n }\n }, [searchParams, canManage, setSearchParams]);\n\n // ...\n}\n```\n\n## Complete Example: Incident Plugin\n\nHere's how the incident plugin registers its commands:\n\n```typescript\n// incident-backend/src/index.ts\nimport { registerSearchProvider } from \"@checkstack/command-backend\";\nimport {\n pluginMetadata,\n access,\n incidentRoutes,\n} from \"@checkstack/incident-common\";\nimport { resolveRoute } from \"@checkstack/common\";\n\nexport default createBackendPlugin({\n metadata: pluginMetadata,\n register(env) {\n env.registerInit({\n deps: { /* ... */ },\n init: async ({ /* ... */ }) => {\n // ... other initialization ...\n\n // Register command palette entries\n registerSearchProvider({\n pluginMetadata,\n commands: [\n {\n id: \"create\",\n title: \"Create Incident\",\n subtitle: \"Report a new incident affecting systems\",\n iconName: \"AlertCircle\",\n shortcuts: [\"meta+shift+i\", \"ctrl+shift+i\"],\n route: resolveRoute(incidentRoutes.routes.config) + \"?action=create\",\n requiredAccessRules: [access.incidentManage],\n },\n ],\n });\n },\n });\n },\n});\n```\n\n## Architecture Notes\n\n### Backend Flow\n\n1. Plugins call `registerSearchProvider()` during initialization\n2. The command backend aggregates all providers\n3. When a search query comes in:\n - All providers are queried in parallel\n - Results are flattened and sorted by priority\n - Results are filtered by user access rules\n - Filtered results are returned to the frontend\n\n### Frontend Flow\n\n1. `GlobalShortcuts` component fetches commands on mount\n2. Global keyboard listeners are registered for all shortcuts\n3. When the palette opens, `search(\"\")` fetches all available items\n4. As the user types, results are filtered by the query\n5. Selecting a result navigates to the specified route\n\n### Access Filtering\n\nResults are filtered twice:\n1. **Backend**: `filterByAccessRules()` removes results the user can't access\n2. **Frontend**: Global shortcuts also check access before triggering\n\nUsers with the `\"*\"` wildcard access bypass all access checks.", "truncated": false }, { @@ -1987,7 +1987,7 @@ export const DOCS_INDEX: readonly DocsIndexEntry[] = [ "The reference implementation", "Next steps" ], - "content": "A master-detail view pairs a scrollable index of items on the left with a detail pane on the right that never moves the list when you pick a row. `@checkstack/ui` ships two primitives for it - `SplitPane` for the layout and `VirtualList` for a windowed index - and the health check run history page is the reference implementation. This page shows how to wire them together.\n\n## When to use it\n\nReach for master-detail when a list is long enough to scroll and each row opens a rich detail view you want to page through without losing your place. Selecting a row swaps the detail pane in place; the master list stays put. On desktop the two columns scroll independently; below `md` the list takes the whole width and the detail opens in a `Sheet` layered over it.\n\nIf your list only needs empty, loading, and error affordances, reach for the [list states](/checkstack/developer-guide/frontend/list-states/) helpers first - master-detail is for the case where the detail pane is a first-class surface of its own.\n\n## The primitives\n\n`SplitPane` is purely structural: it renders two independently-scrolling columns on desktop and master-only below `md`. It brings no chrome of its own, and it deliberately does not own the mobile detail presentation.\n\n```ts\ninterface SplitPaneProps {\n master: React.ReactNode;\n detail: React.ReactNode; // hidden below md\n masterWidth?: \"1/3\" | \"2/5\" | \"1/2\"; // desktop share, default \"2/5\"\n className?: string; // bounds the height; each column scrolls internally\n}\n```\n\n`VirtualList` windows a long list over `@tanstack/react-virtual`, mounting only the visible rows plus an overscan, so a history of thousands of runs stays cheap to scroll. It owns the scroll container, so give it a bounded height through `className`.\n\n```ts\ninterface VirtualListProps {\n items: ReadonlyArray;\n getKey: (props: { item: T; index: number }) => string;\n estimateSize: (props: { item: T; index: number }) => number;\n renderItem: (props: { item: T; index: number }) => React.ReactNode;\n overscan?: number; // default 8\n scrollToIndex?: number; // scrolls this index into view (e.g. the selection)\n className?: string; // must carry a bounded height\n}\n```\n\n## Keep the selection in the URL\n\nThe selected item belongs in the route, not in component state, so deep links and back/forward work. Read the id from the params, and select by navigating - never by `setState`.\n\n```tsx\nimport { useParams, useNavigate } from \"react-router-dom\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport { healthcheckRoutes } from \"@checkstack/healthcheck-common\";\n\nfunction useRunSelection({ systemId, configurationId }: {\n systemId: string;\n configurationId: string;\n}) {\n const { runId } = useParams();\n const navigate = useNavigate();\n\n const selectRun = (nextRunId: string) =>\n navigate(\n resolveRoute(healthcheckRoutes.routes.historyRun, {\n systemId,\n configurationId,\n runId: nextRunId,\n }),\n );\n\n const clearSelection = () =>\n navigate(\n resolveRoute(healthcheckRoutes.routes.historyDetail, {\n systemId,\n configurationId,\n }),\n { replace: true },\n );\n\n return { runId, selectRun, clearSelection };\n}\n```\n\n## Putting it together\n\nThe master column renders a `VirtualList` and scrolls the selected row into view; the detail column renders a sticky detail component with previous/next navigation. Below `md`, `SplitPane` hides the detail column, so render the detail a second time inside a `Sheet` gated on a selection.\n\n```tsx\nimport {\n SplitPane,\n VirtualList,\n EmptyState,\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetBody,\n useIsMobile,\n} from \"@checkstack/ui\";\n\n// Your own chrome-free detail component, keyed by the selected id.\nimport { MyDetailPanel } from \"./MyDetailPanel\";\n\nfunction RunHistory({ systemId, configurationId, runs }: {\n systemId: string;\n configurationId: string;\n runs: ReadonlyArray<{ id: string; status: string; startedAt: Date }>;\n}) {\n const isMobile = useIsMobile();\n const { runId, selectRun, clearSelection } = useRunSelection({\n systemId,\n configurationId,\n });\n const selectedIndex = runs.findIndex((r) => r.id === runId);\n\n const list = (\n item.id}\n estimateSize={() => 56}\n scrollToIndex={selectedIndex === -1 ? undefined : selectedIndex}\n className=\"max-h-full\"\n renderItem={({ item }) => (\n selectRun(item.id)}\n aria-current={item.id === runId}\n className=\"w-full px-3 py-2 text-left\"\n >\n {item.status}\n \n )}\n />\n );\n\n const detail = runId ? (\n \n ) : (\n \n );\n\n return (\n <>\n \n\n {/* Below md the detail column is hidden; the caller owns the Sheet.\n Gate it on the viewport so desktop keeps the split pane only. */}\n {isMobile && (\n !open && clearSelection()}>\n \n \n Run detail\n \n \n {runId && }\n \n \n \n )}\n \n );\n}\n```\n\n## The reference implementation\n\nThe health check run history page (`HealthCheckHistoryDetailPage`) is the pattern in full. It adds the touches a real history view needs:\n\n- A **virtualized master list** (`RunHistoryList`) of raw runs, ending on the last page with a retention divider and the aggregate-bucket rows that survive it. Run rows are keyboard-operable: Enter or Space selects, and ArrowUp/ArrowDown walk to the newer/older run.\n- A shared **`RunDetailPanel`** in the detail column with previous/next buttons, ArrowUp/ArrowDown keyboard navigation, and Charts, Assertions, and Raw-payload tabs. Prev/next crosses page boundaries, flipping the page and selecting its first or last run once it arrives.\n- A **retention message** when a deep-linked run has aged out of the raw window, instead of a blank pane.\n- The **selection in the URL** (the `historyRun` route), so a link to a specific run opens straight to it.\n\n`RunDetailPanel` is chrome-free by design, so the same component drops into the desktop split pane, the mobile `Sheet`, and the check drawer's nested detail sheet without change:\n\n```ts\ninterface RunDetailPanelProps {\n runId: string; // fetches getRunById itself\n strategyId?: string; // for the per-run auto-chart tiles\n onDismiss?: () => void; // renders an X (omit inside a Sheet with its own)\n prevNext?: { onPrev?: () => void; onNext?: () => void };\n}\n```\n\n## Next steps\n\n- [Health check charts](/checkstack/developer-guide/frontend/healthcheck-charts/) - the auto tile grid `RunDetailPanel` renders in its Charts tab.\n- [List states](/checkstack/developer-guide/frontend/list-states/) - empty, loading, and error affordances for the master list.\n- [Performance and accessibility](/checkstack/developer-guide/frontend/performance/) - the `isLowPower` fallbacks the chart primitives honor.\n", + "content": "A master-detail view pairs a scrollable index of items on the left with a detail pane on the right that never moves the list when you pick a row. `@checkstack/ui` ships two primitives for it - `SplitPane` for the layout and `VirtualList` for a windowed index - and the health check run history page is the reference implementation. This page shows how to wire them together.\n\n## When to use it\n\nReach for master-detail when a list is long enough to scroll and each row opens a rich detail view you want to page through without losing your place. Selecting a row swaps the detail pane in place; the master list stays put. On desktop the two columns scroll independently; below `md` the list takes the whole width and the detail opens in a `Sheet` layered over it.\n\nIf your list only needs empty, loading, and error affordances, reach for the [list states](/checkstack/developer-guide/frontend/list-states/) helpers first - master-detail is for the case where the detail pane is a first-class surface of its own.\n\n## The primitives\n\n`SplitPane` is purely structural: it renders two independently-scrolling columns on desktop and master-only below `md`. It brings no chrome of its own, and it deliberately does not own the mobile detail presentation.\n\n```ts\ninterface SplitPaneProps {\n master: React.ReactNode;\n detail: React.ReactNode; // hidden below md\n masterWidth?: \"1/3\" | \"2/5\" | \"1/2\"; // desktop share, default \"2/5\"\n className?: string; // bounds the height; each column scrolls internally\n}\n```\n\n`VirtualList` windows a long list over `@tanstack/react-virtual`, mounting only the visible rows plus an overscan, so a history of thousands of runs stays cheap to scroll. It owns the scroll container, so give it a bounded height through `className`.\n\n```ts\ninterface VirtualListProps {\n items: ReadonlyArray;\n getKey: (props: { item: T; index: number }) => string;\n estimateSize: (props: { item: T; index: number }) => number;\n renderItem: (props: { item: T; index: number }) => React.ReactNode;\n overscan?: number; // default 8\n scrollToIndex?: number; // scrolls this index into view (e.g. the selection)\n className?: string; // must carry a bounded height\n}\n```\n\n## Keep the selection in the URL\n\nThe selected item belongs in the route, not in component state, so deep links and back/forward work. Read the id from the params, and select by navigating - never by `setState`.\n\n```tsx\nimport { useParams, useNavigate } from \"react-router\";\nimport { resolveRoute } from \"@checkstack/common\";\nimport { healthcheckRoutes } from \"@checkstack/healthcheck-common\";\n\nfunction useRunSelection({ systemId, configurationId }: {\n systemId: string;\n configurationId: string;\n}) {\n const { runId } = useParams();\n const navigate = useNavigate();\n\n const selectRun = (nextRunId: string) =>\n navigate(\n resolveRoute(healthcheckRoutes.routes.historyRun, {\n systemId,\n configurationId,\n runId: nextRunId,\n }),\n );\n\n const clearSelection = () =>\n navigate(\n resolveRoute(healthcheckRoutes.routes.historyDetail, {\n systemId,\n configurationId,\n }),\n { replace: true },\n );\n\n return { runId, selectRun, clearSelection };\n}\n```\n\n## Putting it together\n\nThe master column renders a `VirtualList` and scrolls the selected row into view; the detail column renders a sticky detail component with previous/next navigation. Below `md`, `SplitPane` hides the detail column, so render the detail a second time inside a `Sheet` gated on a selection.\n\n```tsx\nimport {\n SplitPane,\n VirtualList,\n EmptyState,\n Sheet,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetBody,\n useIsMobile,\n} from \"@checkstack/ui\";\n\n// Your own chrome-free detail component, keyed by the selected id.\nimport { MyDetailPanel } from \"./MyDetailPanel\";\n\nfunction RunHistory({ systemId, configurationId, runs }: {\n systemId: string;\n configurationId: string;\n runs: ReadonlyArray<{ id: string; status: string; startedAt: Date }>;\n}) {\n const isMobile = useIsMobile();\n const { runId, selectRun, clearSelection } = useRunSelection({\n systemId,\n configurationId,\n });\n const selectedIndex = runs.findIndex((r) => r.id === runId);\n\n const list = (\n item.id}\n estimateSize={() => 56}\n scrollToIndex={selectedIndex === -1 ? undefined : selectedIndex}\n className=\"max-h-full\"\n renderItem={({ item }) => (\n selectRun(item.id)}\n aria-current={item.id === runId}\n className=\"w-full px-3 py-2 text-left\"\n >\n {item.status}\n \n )}\n />\n );\n\n const detail = runId ? (\n \n ) : (\n \n );\n\n return (\n <>\n \n\n {/* Below md the detail column is hidden; the caller owns the Sheet.\n Gate it on the viewport so desktop keeps the split pane only. */}\n {isMobile && (\n !open && clearSelection()}>\n \n \n Run detail\n \n \n {runId && }\n \n \n \n )}\n \n );\n}\n```\n\n## The reference implementation\n\nThe health check run history page (`HealthCheckHistoryDetailPage`) is the pattern in full. It adds the touches a real history view needs:\n\n- A **virtualized master list** (`RunHistoryList`) of raw runs, ending on the last page with a retention divider and the aggregate-bucket rows that survive it. Run rows are keyboard-operable: Enter or Space selects, and ArrowUp/ArrowDown walk to the newer/older run.\n- A shared **`RunDetailPanel`** in the detail column with previous/next buttons, ArrowUp/ArrowDown keyboard navigation, and Charts, Assertions, and Raw-payload tabs. Prev/next crosses page boundaries, flipping the page and selecting its first or last run once it arrives.\n- A **retention message** when a deep-linked run has aged out of the raw window, instead of a blank pane.\n- The **selection in the URL** (the `historyRun` route), so a link to a specific run opens straight to it.\n\n`RunDetailPanel` is chrome-free by design, so the same component drops into the desktop split pane, the mobile `Sheet`, and the check drawer's nested detail sheet without change:\n\n```ts\ninterface RunDetailPanelProps {\n runId: string; // fetches getRunById itself\n strategyId?: string; // for the per-run auto-chart tiles\n onDismiss?: () => void; // renders an X (omit inside a Sheet with its own)\n prevNext?: { onPrev?: () => void; onNext?: () => void };\n}\n```\n\n## Next steps\n\n- [Health check charts](/checkstack/developer-guide/frontend/healthcheck-charts/) - the auto tile grid `RunDetailPanel` renders in its Charts tab.\n- [List states](/checkstack/developer-guide/frontend/list-states/) - empty, loading, and error affordances for the master list.\n- [Performance and accessibility](/checkstack/developer-guide/frontend/performance/) - the `isLowPower` fallbacks the chart primitives honor.\n", "truncated": false }, { @@ -3698,4 +3698,4 @@ export const DOCS_INDEX: readonly DocsIndexEntry[] = [ ]; /** A content hash of the source tree, so a CI check can detect drift. */ -export const DOCS_INDEX_HASH = "ce6ba3303b7dc7713073d7e87d42bf475d144230dfbc755b04314324fbaad031"; +export const DOCS_INDEX_HASH = "d0adce4a0ce1235c480c65e3731fe7ecd42a034f64752f498c7fadeb5c5de18a"; diff --git a/core/ai-frontend/package.json b/core/ai-frontend/package.json index 673f573e7..d37f6326f 100644 --- a/core/ai-frontend/package.json +++ b/core/ai-frontend/package.json @@ -25,7 +25,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/announcement-frontend/package.json b/core/announcement-frontend/package.json index 469588f07..e6b53488c 100644 --- a/core/announcement-frontend/package.json +++ b/core/announcement-frontend/package.json @@ -29,7 +29,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1" }, "devDependencies": { diff --git a/core/announcement-frontend/vite.config.ts b/core/announcement-frontend/vite.config.ts index 686ea816d..01c902e25 100644 --- a/core/announcement-frontend/vite.config.ts +++ b/core/announcement-frontend/vite.config.ts @@ -45,7 +45,7 @@ export default defineConfig({ // the host's instance, or the host's `useStatusWidgetRenderers` would not // see this remote's `defineStatusWidgetRenderer` registration). // - // We deliberately do NOT share react-dom / react-router-dom / + // We deliberately do NOT share react-dom / react-router / // react-query here. This is a PURE, prop-only renderer that renders none // of them; they appear only as DEAD code transitively re-exported by // bundled `*-common` packages / the UI surface. The @module-federation/ diff --git a/core/anomaly-frontend/package.json b/core/anomaly-frontend/package.json index 16ec7e997..47940d9f8 100644 --- a/core/anomaly-frontend/package.json +++ b/core/anomaly-frontend/package.json @@ -29,7 +29,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1" }, "devDependencies": { diff --git a/core/anomaly-frontend/src/components/SystemAnomalyWidget.tsx b/core/anomaly-frontend/src/components/SystemAnomalyWidget.tsx index ee67ade70..523b54341 100644 --- a/core/anomaly-frontend/src/components/SystemAnomalyWidget.tsx +++ b/core/anomaly-frontend/src/components/SystemAnomalyWidget.tsx @@ -36,7 +36,7 @@ import { Layers, } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { anomalyToneStyles, toneForState, diff --git a/core/api-docs-frontend/package.json b/core/api-docs-frontend/package.json index 098c203f0..40eefcad7 100644 --- a/core/api-docs-frontend/package.json +++ b/core/api-docs-frontend/package.json @@ -21,7 +21,7 @@ "@checkstack/api-docs-common": "workspace:*", "@checkstack/ui": "workspace:*", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "lucide-react": "^1.17.0" }, "devDependencies": { diff --git a/core/auth-frontend/package.json b/core/auth-frontend/package.json index 4d274bcc7..8ce94b5ec 100644 --- a/core/auth-frontend/package.json +++ b/core/auth-frontend/package.json @@ -29,7 +29,7 @@ "@checkstack/healthcheck-common": "workspace:*", "@checkstack/ui": "workspace:*", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "lucide-react": "^1.17.0", "better-auth": "^1.6.13", "@checkstack/auth-common": "workspace:*" diff --git a/core/auth-frontend/src/components/AuthErrorPage.tsx b/core/auth-frontend/src/components/AuthErrorPage.tsx index c0e2620ee..519703f01 100644 --- a/core/auth-frontend/src/components/AuthErrorPage.tsx +++ b/core/auth-frontend/src/components/AuthErrorPage.tsx @@ -1,4 +1,4 @@ -import { Link, useSearchParams } from "react-router-dom"; +import { Link, useSearchParams } from "react-router"; import { AlertCircle, Home, LogIn } from "lucide-react"; import { authRoutes } from "@checkstack/auth-common"; import { resolveRoute } from "@checkstack/common"; diff --git a/core/auth-frontend/src/components/AuthSettingsPage.tsx b/core/auth-frontend/src/components/AuthSettingsPage.tsx index 7e7d5765d..c8b0777db 100644 --- a/core/auth-frontend/src/components/AuthSettingsPage.tsx +++ b/core/auth-frontend/src/components/AuthSettingsPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState, useMemo } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { useApi, accessApiRef, diff --git a/core/auth-frontend/src/components/ChangePasswordPage.tsx b/core/auth-frontend/src/components/ChangePasswordPage.tsx index 953782b0b..dc3ce09b4 100644 --- a/core/auth-frontend/src/components/ChangePasswordPage.tsx +++ b/core/auth-frontend/src/components/ChangePasswordPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { Lock, ArrowLeft, CheckCircle, AlertCircle, Key } from "lucide-react"; import { passwordSchema } from "@checkstack/auth-common"; import { diff --git a/core/auth-frontend/src/components/ForgotPasswordPage.tsx b/core/auth-frontend/src/components/ForgotPasswordPage.tsx index 76fdb2107..e5cdb8548 100644 --- a/core/auth-frontend/src/components/ForgotPasswordPage.tsx +++ b/core/auth-frontend/src/components/ForgotPasswordPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Mail, ArrowLeft, CheckCircle } from "lucide-react"; import { authRoutes } from "@checkstack/auth-common"; import { resolveRoute } from "@checkstack/common"; diff --git a/core/auth-frontend/src/components/LoginPage.tsx b/core/auth-frontend/src/components/LoginPage.tsx index 5c86e974a..b427fdceb 100644 --- a/core/auth-frontend/src/components/LoginPage.tsx +++ b/core/auth-frontend/src/components/LoginPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router"; import { LogIn, LogOut, AlertCircle, ArrowLeft, Lock } from "lucide-react"; import { useApi, diff --git a/core/auth-frontend/src/components/OAuthConsentPage.tsx b/core/auth-frontend/src/components/OAuthConsentPage.tsx index 1ba10095e..f3097ebc2 100644 --- a/core/auth-frontend/src/components/OAuthConsentPage.tsx +++ b/core/auth-frontend/src/components/OAuthConsentPage.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { ShieldCheck, AlertCircle } from "lucide-react"; import { extractErrorMessage } from "@checkstack/common"; import { getCachedRuntimeConfig } from "@checkstack/frontend-api"; diff --git a/core/auth-frontend/src/components/OnboardingCheck.tsx b/core/auth-frontend/src/components/OnboardingCheck.tsx index 0ef3f07e3..1920476ea 100644 --- a/core/auth-frontend/src/components/OnboardingCheck.tsx +++ b/core/auth-frontend/src/components/OnboardingCheck.tsx @@ -1,7 +1,7 @@ import { AuthApi } from "@checkstack/auth-common"; import { usePluginClient } from "@checkstack/frontend-api"; import { useEffect } from "react"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router"; /** * Onboarding guard that redirects to onboarding page if no users exist. diff --git a/core/auth-frontend/src/components/OnboardingPage.tsx b/core/auth-frontend/src/components/OnboardingPage.tsx index 0cc5cef32..3d9bc2e22 100644 --- a/core/auth-frontend/src/components/OnboardingPage.tsx +++ b/core/auth-frontend/src/components/OnboardingPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useMemo } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { User, Lock, Mail, CheckCircle, AlertCircle, Check } from "lucide-react"; import { usePluginClient } from "@checkstack/frontend-api"; import { diff --git a/core/auth-frontend/src/components/ProfilePage.tsx b/core/auth-frontend/src/components/ProfilePage.tsx index 90e4fbeb2..447626077 100644 --- a/core/auth-frontend/src/components/ProfilePage.tsx +++ b/core/auth-frontend/src/components/ProfilePage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { useNavigate, Link } from "react-router-dom"; +import { useNavigate, Link } from "react-router"; import { User, Mail, diff --git a/core/auth-frontend/src/components/RegisterPage.tsx b/core/auth-frontend/src/components/RegisterPage.tsx index 573b2cba0..05de5d621 100644 --- a/core/auth-frontend/src/components/RegisterPage.tsx +++ b/core/auth-frontend/src/components/RegisterPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useMemo } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { AlertCircle } from "lucide-react"; import { useApi, usePluginClient } from "@checkstack/frontend-api"; import { authApiRef } from "../api"; diff --git a/core/auth-frontend/src/components/ResetPasswordPage.tsx b/core/auth-frontend/src/components/ResetPasswordPage.tsx index 731ba5937..e31d99080 100644 --- a/core/auth-frontend/src/components/ResetPasswordPage.tsx +++ b/core/auth-frontend/src/components/ResetPasswordPage.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Link, useSearchParams, useNavigate } from "react-router-dom"; +import { Link, useSearchParams, useNavigate } from "react-router"; import { Lock, ArrowLeft, CheckCircle, AlertCircle } from "lucide-react"; import { AuthApi, authRoutes, passwordSchema } from "@checkstack/auth-common"; import { resolveRoute, extractErrorMessage} from "@checkstack/common"; diff --git a/core/automation-frontend/package.json b/core/automation-frontend/package.json index f188de1f3..4dd838fa4 100644 --- a/core/automation-frontend/package.json +++ b/core/automation-frontend/package.json @@ -36,7 +36,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "yaml": "^2.6.1" }, "devDependencies": { diff --git a/core/automation-frontend/src/components/RunAsServiceAccountPicker.tsx b/core/automation-frontend/src/components/RunAsServiceAccountPicker.tsx index 332a051a5..8182722dd 100644 --- a/core/automation-frontend/src/components/RunAsServiceAccountPicker.tsx +++ b/core/automation-frontend/src/components/RunAsServiceAccountPicker.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { ExternalLink, KeyRound, ShieldCheck } from "lucide-react"; import { usePluginClient } from "@checkstack/frontend-api"; import { AuthApi, authRoutes } from "@checkstack/auth-common"; diff --git a/core/automation-frontend/src/editor/action-leaf-cards.tsx b/core/automation-frontend/src/editor/action-leaf-cards.tsx index 0922808f8..755cba4eb 100644 --- a/core/automation-frontend/src/editor/action-leaf-cards.tsx +++ b/core/automation-frontend/src/editor/action-leaf-cards.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { resolveRoute } from "@checkstack/common"; import { integrationRoutes } from "@checkstack/integration-common"; import { diff --git a/core/automation-frontend/src/pages/AutomationEditPage.tsx b/core/automation-frontend/src/pages/AutomationEditPage.tsx index 2f5ab33b5..8e148c032 100644 --- a/core/automation-frontend/src/pages/AutomationEditPage.tsx +++ b/core/automation-frontend/src/pages/AutomationEditPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link, useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { Link, useNavigate, useParams, useSearchParams } from "react-router"; import { ChevronLeft, History as HistoryIcon, diff --git a/core/automation-frontend/src/pages/AutomationListPage.tsx b/core/automation-frontend/src/pages/AutomationListPage.tsx index cc8189a2f..4a218619e 100644 --- a/core/automation-frontend/src/pages/AutomationListPage.tsx +++ b/core/automation-frontend/src/pages/AutomationListPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router"; import { Workflow, Plus, FlaskConical, Trash2, History } from "lucide-react"; import { usePluginClient, diff --git a/core/automation-frontend/src/pages/AutomationTemplatePickerPage.tsx b/core/automation-frontend/src/pages/AutomationTemplatePickerPage.tsx index 82d4005d4..b9b28b6ce 100644 --- a/core/automation-frontend/src/pages/AutomationTemplatePickerPage.tsx +++ b/core/automation-frontend/src/pages/AutomationTemplatePickerPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { Workflow, FilePlus2, ArrowRight } from "lucide-react"; import { usePluginClient, diff --git a/core/automation-frontend/src/pages/RunDetailPage.tsx b/core/automation-frontend/src/pages/RunDetailPage.tsx index 2c8ce9532..e4dcd5096 100644 --- a/core/automation-frontend/src/pages/RunDetailPage.tsx +++ b/core/automation-frontend/src/pages/RunDetailPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link, useParams } from "react-router-dom"; +import { Link, useParams } from "react-router"; import { CheckCircle2, ChevronLeft, diff --git a/core/automation-frontend/src/pages/RunsPage.tsx b/core/automation-frontend/src/pages/RunsPage.tsx index bdf83f88d..8041464d6 100644 --- a/core/automation-frontend/src/pages/RunsPage.tsx +++ b/core/automation-frontend/src/pages/RunsPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link, useParams, useNavigate } from "react-router-dom"; +import { Link, useParams, useNavigate } from "react-router"; import { History, ChevronLeft, ArrowUpRight } from "lucide-react"; import { usePluginClient, diff --git a/core/backend/src/index.ts b/core/backend/src/index.ts index 7b7546513..2cef4814a 100644 --- a/core/backend/src/index.ts +++ b/core/backend/src/index.ts @@ -597,7 +597,7 @@ if (frontendDistPath && fs.existsSync(frontendDistPath)) { return next(); }); - // Serve vendor scripts (externalized React, react-router-dom, etc.) + // Serve vendor scripts (externalized React, react-router, etc.) app.get("/vendor/*", async (c) => { const vendorPath = c.req.path.replace("/vendor/", ""); const filePath = path.join(frontendDistPath, "vendor", vendorPath); diff --git a/core/catalog-frontend/package.json b/core/catalog-frontend/package.json index 476d74cec..f6361ca6c 100644 --- a/core/catalog-frontend/package.json +++ b/core/catalog-frontend/package.json @@ -30,7 +30,7 @@ "@dnd-kit/utilities": "^3.2.2", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1" }, "devDependencies": { diff --git a/core/catalog-frontend/src/components/CatalogConfigPage.tsx b/core/catalog-frontend/src/components/CatalogConfigPage.tsx index 8682c9d2c..7265855a2 100644 --- a/core/catalog-frontend/src/components/CatalogConfigPage.tsx +++ b/core/catalog-frontend/src/components/CatalogConfigPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { useApi, accessApiRef, diff --git a/core/catalog-frontend/src/components/CatalogPage.tsx b/core/catalog-frontend/src/components/CatalogPage.tsx index 75591f176..ec9686763 100644 --- a/core/catalog-frontend/src/components/CatalogPage.tsx +++ b/core/catalog-frontend/src/components/CatalogPage.tsx @@ -22,7 +22,7 @@ import { Button, } from "@checkstack/ui"; import { Layers, ArrowRight, Plus } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { useCatalogBrowseState } from "../hooks/useCatalogBrowseState"; import { CatalogBrowseToolbar } from "./browse/CatalogBrowseToolbar"; import { CatalogGroupSection } from "./browse/CatalogGroupSection"; diff --git a/core/catalog-frontend/src/components/LinkedStreamsCard.tsx b/core/catalog-frontend/src/components/LinkedStreamsCard.tsx index e04c5d54c..a79bdbae4 100644 --- a/core/catalog-frontend/src/components/LinkedStreamsCard.tsx +++ b/core/catalog-frontend/src/components/LinkedStreamsCard.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { CollapsibleDetailCard } from "@checkstack/ui"; import { ChevronRight, type LucideIcon } from "lucide-react"; diff --git a/core/catalog-frontend/src/components/SystemDetailPage.tsx b/core/catalog-frontend/src/components/SystemDetailPage.tsx index 26cb0610a..1a198305b 100644 --- a/core/catalog-frontend/src/components/SystemDetailPage.tsx +++ b/core/catalog-frontend/src/components/SystemDetailPage.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; import { usePluginClient, ExtensionSlot, diff --git a/core/catalog-frontend/src/components/SystemEditor.tsx b/core/catalog-frontend/src/components/SystemEditor.tsx index dfa51ab83..023bd7d7a 100644 --- a/core/catalog-frontend/src/components/SystemEditor.tsx +++ b/core/catalog-frontend/src/components/SystemEditor.tsx @@ -19,7 +19,7 @@ import { useSeedFormOnOpen, } from "@checkstack/ui"; import { Layers } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { TeamOwnershipPicker, teamCreateErrorMessage, diff --git a/core/catalog-frontend/src/components/browse/CatalogSystemRow.tsx b/core/catalog-frontend/src/components/browse/CatalogSystemRow.tsx index 2e1dbe2d8..23e4e8d68 100644 --- a/core/catalog-frontend/src/components/browse/CatalogSystemRow.tsx +++ b/core/catalog-frontend/src/components/browse/CatalogSystemRow.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { ExtensionSlot } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { diff --git a/core/catalog-frontend/src/hooks/useCatalogBrowseState.ts b/core/catalog-frontend/src/hooks/useCatalogBrowseState.ts index 61f25da0d..932f50165 100644 --- a/core/catalog-frontend/src/hooks/useCatalogBrowseState.ts +++ b/core/catalog-frontend/src/hooks/useCatalogBrowseState.ts @@ -1,5 +1,5 @@ import { useCallback, useMemo } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { useDataTableFilters, type DataTableFiltersApi, diff --git a/core/command-frontend/package.json b/core/command-frontend/package.json index 0f3069c25..b412ebf89 100644 --- a/core/command-frontend/package.json +++ b/core/command-frontend/package.json @@ -18,7 +18,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.7.2", diff --git a/core/command-frontend/src/components/SearchDialog.tsx b/core/command-frontend/src/components/SearchDialog.tsx index 04ebec36a..5bc247912 100644 --- a/core/command-frontend/src/components/SearchDialog.tsx +++ b/core/command-frontend/src/components/SearchDialog.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useCallback, useRef } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { Dialog, DialogContent, diff --git a/core/dashboard-frontend/package.json b/core/dashboard-frontend/package.json index 67451a6e4..e5ea0782e 100644 --- a/core/dashboard-frontend/package.json +++ b/core/dashboard-frontend/package.json @@ -34,7 +34,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/dashboard-frontend/src/components/DashboardSystemHealthSection.tsx b/core/dashboard-frontend/src/components/DashboardSystemHealthSection.tsx index bfa1b3b1a..bd3a117ec 100644 --- a/core/dashboard-frontend/src/components/DashboardSystemHealthSection.tsx +++ b/core/dashboard-frontend/src/components/DashboardSystemHealthSection.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { Link, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router"; import { usePluginClient, ExtensionSlot, diff --git a/core/dashboard-frontend/src/components/DashboardWelcomeSection.tsx b/core/dashboard-frontend/src/components/DashboardWelcomeSection.tsx index 5cb4ee2e9..6df9eeb92 100644 --- a/core/dashboard-frontend/src/components/DashboardWelcomeSection.tsx +++ b/core/dashboard-frontend/src/components/DashboardWelcomeSection.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { useApi, accessApiRef, usePluginClient } from "@checkstack/frontend-api"; import { CatalogApi, catalogRoutes, catalogAccess } from "@checkstack/catalog-common"; import { resolveRoute } from "@checkstack/common"; diff --git a/core/dashboard-frontend/src/components/GettingStartedChecklist.tsx b/core/dashboard-frontend/src/components/GettingStartedChecklist.tsx index 9015b0b31..f1e8fe9db 100644 --- a/core/dashboard-frontend/src/components/GettingStartedChecklist.tsx +++ b/core/dashboard-frontend/src/components/GettingStartedChecklist.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Card, CardContent, cn } from "@checkstack/ui"; import { resolveRoute } from "@checkstack/common"; import { useTipState } from "@checkstack/tips-frontend"; diff --git a/core/dashboard-frontend/src/components/ProblemSystemCard.tsx b/core/dashboard-frontend/src/components/ProblemSystemCard.tsx index cc8b0c6b1..4077a6edf 100644 --- a/core/dashboard-frontend/src/components/ProblemSystemCard.tsx +++ b/core/dashboard-frontend/src/components/ProblemSystemCard.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { formatDistanceToNow } from "date-fns"; import { resolveRoute, type AccessRule } from "@checkstack/common"; import { useApi, accessApiRef } from "@checkstack/frontend-api"; diff --git a/core/dependency-frontend/package.json b/core/dependency-frontend/package.json index 7e8680c95..2054e99be 100644 --- a/core/dependency-frontend/package.json +++ b/core/dependency-frontend/package.json @@ -29,7 +29,7 @@ "@xyflow/react": "^12.11.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@checkstack/scripts": "workspace:*", diff --git a/core/dependency-frontend/src/components/DependencyEditor.tsx b/core/dependency-frontend/src/components/DependencyEditor.tsx index 2a37b162f..1fcbd3323 100644 --- a/core/dependency-frontend/src/components/DependencyEditor.tsx +++ b/core/dependency-frontend/src/components/DependencyEditor.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, type SlotContext, diff --git a/core/dependency-frontend/src/components/SystemDependenciesPanel.tsx b/core/dependency-frontend/src/components/SystemDependenciesPanel.tsx index c9a3a460d..f820c35ec 100644 --- a/core/dependency-frontend/src/components/SystemDependenciesPanel.tsx +++ b/core/dependency-frontend/src/components/SystemDependenciesPanel.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, useApi, diff --git a/core/frontend/package.json b/core/frontend/package.json index dffdb7ce6..e62582cb3 100644 --- a/core/frontend/package.json +++ b/core/frontend/package.json @@ -47,7 +47,7 @@ "lucide-react": "^1.17.0", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "tailwind-merge": "^2.2.0", "tailwindcss": "^3.4.1", "tailwindcss-animate": "^1.0.7" diff --git a/core/frontend/src/App.tsx b/core/frontend/src/App.tsx index 0debb1795..ac70fd380 100644 --- a/core/frontend/src/App.tsx +++ b/core/frontend/src/App.tsx @@ -11,7 +11,7 @@ import { Link, Outlet, useNavigate, -} from "react-router-dom"; +} from "react-router"; import { Menu, AlertTriangle } from "lucide-react"; import type { AccessRule } from "@checkstack/common"; import { diff --git a/core/frontend/src/components/Sidebar.tsx b/core/frontend/src/components/Sidebar.tsx index 06347f2c9..25e6ed3d5 100644 --- a/core/frontend/src/components/Sidebar.tsx +++ b/core/frontend/src/components/Sidebar.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useState } from "react"; -import { NavLink, useLocation } from "react-router-dom"; +import { NavLink, useLocation } from "react-router"; import { pluginRegistry } from "@checkstack/frontend-api"; import { useAccessRules, useManageableTypes } from "@checkstack/auth-frontend"; import { openSearchPalette } from "@checkstack/command-frontend"; diff --git a/core/frontend/src/public-app.tsx b/core/frontend/src/public-app.tsx index 3a50d6dfa..6a41bbd30 100644 --- a/core/frontend/src/public-app.tsx +++ b/core/frontend/src/public-app.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { MemoryRouter, Routes, Route, useParams } from "react-router-dom"; +import { MemoryRouter, Routes, Route, useParams } from "react-router"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { ApiProvider, diff --git a/core/frontend/vite.config.ts b/core/frontend/vite.config.ts index 79d929817..6b598634d 100644 --- a/core/frontend/vite.config.ts +++ b/core/frontend/vite.config.ts @@ -109,10 +109,10 @@ export default defineConfig(({ command }) => { const shared: Record = { react: { singleton: true, eager: true, requiredVersion: "^19.0.0" }, "react-dom": { singleton: true, eager: true, requiredVersion: "^19.0.0" }, - "react-router-dom": { + "react-router": { singleton: true, eager: true, - requiredVersion: "^7.0.0", + requiredVersion: "^8.0.0", }, "@tanstack/react-query": { singleton: true, @@ -202,7 +202,7 @@ export default defineConfig(({ command }) => { include: [ "react", "react-dom", - "react-router-dom", + "react-router", // NOTE (migration stage 1): the Monaco / VS Code editor stack // (@typefox/monaco-editor-react, monaco-languageclient, @codingame/*) // is intentionally NOT listed here. Those packages live in @@ -235,7 +235,7 @@ export default defineConfig(({ command }) => { dedupe: [ "react", "react-dom", - "react-router-dom", + "react-router", "react/jsx-runtime", "@tanstack/react-query", ], diff --git a/core/gitops-frontend/package.json b/core/gitops-frontend/package.json index 80f28aa02..efee3ab91 100644 --- a/core/gitops-frontend/package.json +++ b/core/gitops-frontend/package.json @@ -23,7 +23,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/healthcheck-frontend/package.json b/core/healthcheck-frontend/package.json index e77f8e1ba..b5deac58a 100644 --- a/core/healthcheck-frontend/package.json +++ b/core/healthcheck-frontend/package.json @@ -36,7 +36,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "uuid": "^14.0.0", "zod": "^4.2.1" }, diff --git a/core/healthcheck-frontend/src/components/HealthCheckDrawer.tsx b/core/healthcheck-frontend/src/components/HealthCheckDrawer.tsx index 928a04c8f..1fdc95902 100644 --- a/core/healthcheck-frontend/src/components/HealthCheckDrawer.tsx +++ b/core/healthcheck-frontend/src/components/HealthCheckDrawer.tsx @@ -51,7 +51,7 @@ import { cn, } from "@checkstack/ui"; import { format, formatDistanceToNow } from "date-fns"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { HealthCheckLatencyChart } from "./HealthCheckLatencyChart"; import { HealthCheckSparkline } from "./HealthCheckSparkline"; import { HealthCheckRunsTable } from "./HealthCheckRunsTable"; diff --git a/core/healthcheck-frontend/src/components/HealthCheckSystemOverview.tsx b/core/healthcheck-frontend/src/components/HealthCheckSystemOverview.tsx index 388fa9a13..224dce493 100644 --- a/core/healthcheck-frontend/src/components/HealthCheckSystemOverview.tsx +++ b/core/healthcheck-frontend/src/components/HealthCheckSystemOverview.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, lazy, Suspense } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { usePluginClient, type SlotContext, diff --git a/core/healthcheck-frontend/src/components/SystemHealthCheckAssignment.tsx b/core/healthcheck-frontend/src/components/SystemHealthCheckAssignment.tsx index 155587371..55affd997 100644 --- a/core/healthcheck-frontend/src/components/SystemHealthCheckAssignment.tsx +++ b/core/healthcheck-frontend/src/components/SystemHealthCheckAssignment.tsx @@ -8,7 +8,7 @@ import { import { HealthCheckApi } from "@checkstack/healthcheck-common"; import { RowAction } from "@checkstack/ui"; import { Activity } from "lucide-react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { CatalogSystemActionsSlot, catalogAccess, diff --git a/core/healthcheck-frontend/src/components/assignments/GeneralPanel.tsx b/core/healthcheck-frontend/src/components/assignments/GeneralPanel.tsx index b3ad9f646..78c91b740 100644 --- a/core/healthcheck-frontend/src/components/assignments/GeneralPanel.tsx +++ b/core/healthcheck-frontend/src/components/assignments/GeneralPanel.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Button, Checkbox, Label } from "@checkstack/ui"; import { ExternalLink, Trash2 } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { resolveRoute } from "@checkstack/common"; import { catalogRoutes } from "@checkstack/catalog-common"; diff --git a/core/healthcheck-frontend/src/pages/HealthCheckConfigPage.tsx b/core/healthcheck-frontend/src/pages/HealthCheckConfigPage.tsx index 1b95adb32..21c8fdc87 100644 --- a/core/healthcheck-frontend/src/pages/HealthCheckConfigPage.tsx +++ b/core/healthcheck-frontend/src/pages/HealthCheckConfigPage.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo } from "react"; -import { useSearchParams, useNavigate } from "react-router-dom"; +import { useSearchParams, useNavigate } from "react-router"; import { usePluginClient, useQueryClient, @@ -38,7 +38,7 @@ import { toastError, } from "@checkstack/ui"; import { Plus, History, Activity, ExternalLink, Sparkles } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { resolveRoute, APP_DOC_SLUGS, docsPath } from "@checkstack/common"; /** diff --git a/core/healthcheck-frontend/src/pages/HealthCheckHistoryDetailPage.tsx b/core/healthcheck-frontend/src/pages/HealthCheckHistoryDetailPage.tsx index c3c58ccfc..e5e5cb4fb 100644 --- a/core/healthcheck-frontend/src/pages/HealthCheckHistoryDetailPage.tsx +++ b/core/healthcheck-frontend/src/pages/HealthCheckHistoryDetailPage.tsx @@ -43,7 +43,7 @@ import { cn, type DateRange, } from "@checkstack/ui"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { History, MousePointerClick } from "lucide-react"; import { type HealthCheckRunDetailed } from "../components/HealthCheckRunsTable"; import { RunHistoryList } from "../components/RunHistoryList"; diff --git a/core/healthcheck-frontend/src/pages/HealthCheckHistoryPage.tsx b/core/healthcheck-frontend/src/pages/HealthCheckHistoryPage.tsx index 52553226e..cd50915a1 100644 --- a/core/healthcheck-frontend/src/pages/HealthCheckHistoryPage.tsx +++ b/core/healthcheck-frontend/src/pages/HealthCheckHistoryPage.tsx @@ -26,7 +26,7 @@ import { } from "@checkstack/healthcheck-common"; import { catalogResourceTypes } from "@checkstack/catalog-common"; import { resolveRoute } from "@checkstack/common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { History } from "lucide-react"; const HealthCheckHistoryPageContent = () => { diff --git a/core/healthcheck-frontend/src/pages/HealthCheckIDEPage.tsx b/core/healthcheck-frontend/src/pages/HealthCheckIDEPage.tsx index 9fd0d071e..4981d28dc 100644 --- a/core/healthcheck-frontend/src/pages/HealthCheckIDEPage.tsx +++ b/core/healthcheck-frontend/src/pages/HealthCheckIDEPage.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, useMemo, useEffect, useRef } from "react"; -import { useParams, useSearchParams, useNavigate } from "react-router-dom"; +import { useParams, useSearchParams, useNavigate } from "react-router"; import { usePluginClient, wrapInSuspense, diff --git a/core/healthcheck-frontend/src/pages/StrategyPickerPage.tsx b/core/healthcheck-frontend/src/pages/StrategyPickerPage.tsx index b32fe5911..de67ca62b 100644 --- a/core/healthcheck-frontend/src/pages/StrategyPickerPage.tsx +++ b/core/healthcheck-frontend/src/pages/StrategyPickerPage.tsx @@ -16,7 +16,7 @@ import { Badge, } from "@checkstack/ui"; import { Search, Zap } from "lucide-react"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useNavigate, useSearchParams } from "react-router"; import { resolveRoute } from "@checkstack/common"; /** diff --git a/core/incident-frontend/package.json b/core/incident-frontend/package.json index 40b07a15c..3c4e7af14 100644 --- a/core/incident-frontend/package.json +++ b/core/incident-frontend/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/incident-frontend/src/components/IncidentEditor.tsx b/core/incident-frontend/src/components/IncidentEditor.tsx index 80b9a60ca..72b8c3730 100644 --- a/core/incident-frontend/src/components/IncidentEditor.tsx +++ b/core/incident-frontend/src/components/IncidentEditor.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, useApi, diff --git a/core/incident-frontend/src/components/SystemIncidentPanel.tsx b/core/incident-frontend/src/components/SystemIncidentPanel.tsx index f5a0b735f..10f97b84e 100644 --- a/core/incident-frontend/src/components/SystemIncidentPanel.tsx +++ b/core/incident-frontend/src/components/SystemIncidentPanel.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, type SlotContext } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { SystemDetailsTopSlot } from "@checkstack/catalog-common"; diff --git a/core/incident-frontend/src/pages/IncidentConfigPage.tsx b/core/incident-frontend/src/pages/IncidentConfigPage.tsx index 7b27e5efc..f4035dcef 100644 --- a/core/incident-frontend/src/pages/IncidentConfigPage.tsx +++ b/core/incident-frontend/src/pages/IncidentConfigPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { usePluginClient, accessApiRef, diff --git a/core/incident-frontend/src/pages/IncidentDetailPage.tsx b/core/incident-frontend/src/pages/IncidentDetailPage.tsx index 1cc6c3b49..fcfefc034 100644 --- a/core/incident-frontend/src/pages/IncidentDetailPage.tsx +++ b/core/incident-frontend/src/pages/IncidentDetailPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useParams, useNavigate, useSearchParams } from "react-router-dom"; +import { useParams, useNavigate, useSearchParams } from "react-router"; import { usePluginClient, accessApiRef, diff --git a/core/incident-frontend/src/pages/IncidentOverviewPage.tsx b/core/incident-frontend/src/pages/IncidentOverviewPage.tsx index 85abad979..97f4fe45f 100644 --- a/core/incident-frontend/src/pages/IncidentOverviewPage.tsx +++ b/core/incident-frontend/src/pages/IncidentOverviewPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, wrapInSuspense } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { IncidentApi } from "../api"; diff --git a/core/incident-frontend/src/pages/SystemIncidentHistoryPage.tsx b/core/incident-frontend/src/pages/SystemIncidentHistoryPage.tsx index 85a06027b..d5c5e0796 100644 --- a/core/incident-frontend/src/pages/SystemIncidentHistoryPage.tsx +++ b/core/incident-frontend/src/pages/SystemIncidentHistoryPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { usePluginClient, wrapInSuspense } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { IncidentApi } from "../api"; diff --git a/core/infrastructure-frontend/package.json b/core/infrastructure-frontend/package.json index 703dffbef..79e25e1e6 100644 --- a/core/infrastructure-frontend/package.json +++ b/core/infrastructure-frontend/package.json @@ -30,7 +30,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@types/react": "^19.0.0", diff --git a/core/infrastructure-frontend/src/pages/InfrastructureConfigPage.tsx b/core/infrastructure-frontend/src/pages/InfrastructureConfigPage.tsx index db94b0bc8..81025af6a 100644 --- a/core/infrastructure-frontend/src/pages/InfrastructureConfigPage.tsx +++ b/core/infrastructure-frontend/src/pages/InfrastructureConfigPage.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { wrapInSuspense, accessApiRef, diff --git a/core/integration-frontend/package.json b/core/integration-frontend/package.json index aae6668fc..43f29d059 100644 --- a/core/integration-frontend/package.json +++ b/core/integration-frontend/package.json @@ -24,7 +24,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/integration-frontend/src/pages/IntegrationsLandingPage.tsx b/core/integration-frontend/src/pages/IntegrationsLandingPage.tsx index 4d79fc63b..c33db923f 100644 --- a/core/integration-frontend/src/pages/IntegrationsLandingPage.tsx +++ b/core/integration-frontend/src/pages/IntegrationsLandingPage.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { PageLayout, EmptyState, diff --git a/core/integration-frontend/src/pages/ProviderConnectionsPage.tsx b/core/integration-frontend/src/pages/ProviderConnectionsPage.tsx index cf7a14f8a..6dd4f76c6 100644 --- a/core/integration-frontend/src/pages/ProviderConnectionsPage.tsx +++ b/core/integration-frontend/src/pages/ProviderConnectionsPage.tsx @@ -5,7 +5,7 @@ * Uses the provider's connectionSchema with DynamicForm for the configuration UI. */ import { useState } from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { Plus, Settings2, diff --git a/core/logstream-frontend/package.json b/core/logstream-frontend/package.json index b61409917..ec6322983 100644 --- a/core/logstream-frontend/package.json +++ b/core/logstream-frontend/package.json @@ -34,7 +34,7 @@ "@tanstack/react-query": "^5.100.14", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@checkstack/scripts": "workspace:*", diff --git a/core/logstream-frontend/src/components/CorrelatedLogs.tsx b/core/logstream-frontend/src/components/CorrelatedLogs.tsx index 94424f8f2..c1c6988ea 100644 --- a/core/logstream-frontend/src/components/CorrelatedLogs.tsx +++ b/core/logstream-frontend/src/components/CorrelatedLogs.tsx @@ -1,5 +1,5 @@ import { useMemo } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { Card, diff --git a/core/logstream-frontend/src/components/CreateStreamDialog.tsx b/core/logstream-frontend/src/components/CreateStreamDialog.tsx index 1454851b4..5fdbb7fd5 100644 --- a/core/logstream-frontend/src/components/CreateStreamDialog.tsx +++ b/core/logstream-frontend/src/components/CreateStreamDialog.tsx @@ -20,7 +20,7 @@ import { teamCreateErrorMessage, } from "@checkstack/auth-frontend"; import { LogstreamApi, logstreamAccess } from "@checkstack/logstream-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; export interface CreateStreamDialogProps { open: boolean; diff --git a/core/logstream-frontend/src/components/DangerZoneSection.tsx b/core/logstream-frontend/src/components/DangerZoneSection.tsx index 07b99bb69..b7368f7dc 100644 --- a/core/logstream-frontend/src/components/DangerZoneSection.tsx +++ b/core/logstream-frontend/src/components/DangerZoneSection.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { Card, diff --git a/core/logstream-frontend/src/pages/LogStreamDetailPage.tsx b/core/logstream-frontend/src/pages/LogStreamDetailPage.tsx index 651c73ac6..893b4c16d 100644 --- a/core/logstream-frontend/src/pages/LogStreamDetailPage.tsx +++ b/core/logstream-frontend/src/pages/LogStreamDetailPage.tsx @@ -15,7 +15,7 @@ import { Radio, Settings, } from "lucide-react"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router"; import { LogstreamApi, type ImportantEvent } from "@checkstack/logstream-common"; import { EXPLORE_PARAMS, EXPLORE_TAB_VALUE } from "../lib/explore-link"; import { OverviewTab } from "../components/tabs/OverviewTab"; diff --git a/core/logstream-frontend/src/pages/LogStreamListPage.tsx b/core/logstream-frontend/src/pages/LogStreamListPage.tsx index c752fdeca..1e48d041c 100644 --- a/core/logstream-frontend/src/pages/LogStreamListPage.tsx +++ b/core/logstream-frontend/src/pages/LogStreamListPage.tsx @@ -18,7 +18,7 @@ import { type LogStreamSummary, } from "@checkstack/logstream-common"; import { signalScopeMeta } from "@checkstack/signal-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { CreateStreamDialog } from "../components/CreateStreamDialog"; /** diff --git a/core/maintenance-frontend/package.json b/core/maintenance-frontend/package.json index e132b04ca..69fef1134 100644 --- a/core/maintenance-frontend/package.json +++ b/core/maintenance-frontend/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/maintenance-frontend/src/components/DashboardUpcomingMaintenances.tsx b/core/maintenance-frontend/src/components/DashboardUpcomingMaintenances.tsx index 04dfd9d61..faa4cdfc6 100644 --- a/core/maintenance-frontend/src/components/DashboardUpcomingMaintenances.tsx +++ b/core/maintenance-frontend/src/components/DashboardUpcomingMaintenances.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { diff --git a/core/maintenance-frontend/src/components/MaintenanceEditor.tsx b/core/maintenance-frontend/src/components/MaintenanceEditor.tsx index 3a9406145..29eef7bcd 100644 --- a/core/maintenance-frontend/src/components/MaintenanceEditor.tsx +++ b/core/maintenance-frontend/src/components/MaintenanceEditor.tsx @@ -1,5 +1,5 @@ import React, { useId, useState, useEffect } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, useApi, accessApiRef } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { MaintenanceApi } from "../api"; diff --git a/core/maintenance-frontend/src/components/SystemMaintenancePanel.tsx b/core/maintenance-frontend/src/components/SystemMaintenancePanel.tsx index 6681ce433..53565f646 100644 --- a/core/maintenance-frontend/src/components/SystemMaintenancePanel.tsx +++ b/core/maintenance-frontend/src/components/SystemMaintenancePanel.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, type SlotContext } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { SystemDetailsSlot } from "@checkstack/catalog-common"; diff --git a/core/maintenance-frontend/src/pages/MaintenanceConfigPage.tsx b/core/maintenance-frontend/src/pages/MaintenanceConfigPage.tsx index 384c8da90..d620c0128 100644 --- a/core/maintenance-frontend/src/pages/MaintenanceConfigPage.tsx +++ b/core/maintenance-frontend/src/pages/MaintenanceConfigPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useMemo, useState } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { usePluginClient, accessApiRef, diff --git a/core/maintenance-frontend/src/pages/MaintenanceDetailPage.tsx b/core/maintenance-frontend/src/pages/MaintenanceDetailPage.tsx index 697ebab18..d01399f39 100644 --- a/core/maintenance-frontend/src/pages/MaintenanceDetailPage.tsx +++ b/core/maintenance-frontend/src/pages/MaintenanceDetailPage.tsx @@ -4,7 +4,7 @@ import { Link, useNavigate, useSearchParams, -} from "react-router-dom"; +} from "react-router"; import { usePluginClient, wrapInSuspense, diff --git a/core/maintenance-frontend/src/pages/MaintenanceOverviewPage.tsx b/core/maintenance-frontend/src/pages/MaintenanceOverviewPage.tsx index 159376325..68fb74b07 100644 --- a/core/maintenance-frontend/src/pages/MaintenanceOverviewPage.tsx +++ b/core/maintenance-frontend/src/pages/MaintenanceOverviewPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient, wrapInSuspense } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { MaintenanceApi } from "../api"; diff --git a/core/maintenance-frontend/src/pages/SystemMaintenanceHistoryPage.tsx b/core/maintenance-frontend/src/pages/SystemMaintenanceHistoryPage.tsx index 3f8c0b7c3..1676c1312 100644 --- a/core/maintenance-frontend/src/pages/SystemMaintenanceHistoryPage.tsx +++ b/core/maintenance-frontend/src/pages/SystemMaintenanceHistoryPage.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { usePluginClient, wrapInSuspense } from "@checkstack/frontend-api"; import { resolveRoute } from "@checkstack/common"; import { MaintenanceApi } from "../api"; diff --git a/core/metricstream-frontend/package.json b/core/metricstream-frontend/package.json index 406f6789d..fe041bc65 100644 --- a/core/metricstream-frontend/package.json +++ b/core/metricstream-frontend/package.json @@ -35,7 +35,7 @@ "@tanstack/react-query": "^5.100.14", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@checkstack/scripts": "workspace:*", diff --git a/core/metricstream-frontend/src/components/CreateStreamDialog.tsx b/core/metricstream-frontend/src/components/CreateStreamDialog.tsx index b82888427..3aeb518ec 100644 --- a/core/metricstream-frontend/src/components/CreateStreamDialog.tsx +++ b/core/metricstream-frontend/src/components/CreateStreamDialog.tsx @@ -20,7 +20,7 @@ import { teamCreateErrorMessage, } from "@checkstack/auth-frontend"; import { MetricstreamApi, metricstreamAccess } from "@checkstack/metricstream-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; export interface CreateStreamDialogProps { open: boolean; diff --git a/core/metricstream-frontend/src/components/DangerZoneSection.tsx b/core/metricstream-frontend/src/components/DangerZoneSection.tsx index c918c9600..0633470a8 100644 --- a/core/metricstream-frontend/src/components/DangerZoneSection.tsx +++ b/core/metricstream-frontend/src/components/DangerZoneSection.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { Card, diff --git a/core/metricstream-frontend/src/components/MetricQuickChart.tsx b/core/metricstream-frontend/src/components/MetricQuickChart.tsx index deda5ea03..eafa30d39 100644 --- a/core/metricstream-frontend/src/components/MetricQuickChart.tsx +++ b/core/metricstream-frontend/src/components/MetricQuickChart.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { ChartCard, diff --git a/core/metricstream-frontend/src/pages/MetricStreamDetailPage.tsx b/core/metricstream-frontend/src/pages/MetricStreamDetailPage.tsx index 75dfc248c..fba2a2541 100644 --- a/core/metricstream-frontend/src/pages/MetricStreamDetailPage.tsx +++ b/core/metricstream-frontend/src/pages/MetricStreamDetailPage.tsx @@ -8,7 +8,7 @@ import { Skeleton, } from "@checkstack/ui"; import { Gauge, LayoutDashboard, Boxes, Radio, Settings } from "lucide-react"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router"; import { MetricstreamApi } from "@checkstack/metricstream-common"; import { OverviewTab } from "../components/tabs/OverviewTab"; import { MetricsTab } from "../components/tabs/MetricsTab"; diff --git a/core/metricstream-frontend/src/pages/MetricStreamListPage.tsx b/core/metricstream-frontend/src/pages/MetricStreamListPage.tsx index f998facee..80ff23f8a 100644 --- a/core/metricstream-frontend/src/pages/MetricStreamListPage.tsx +++ b/core/metricstream-frontend/src/pages/MetricStreamListPage.tsx @@ -20,7 +20,7 @@ import { type MetricStreamSummary, } from "@checkstack/metricstream-common"; import { signalScopeMeta } from "@checkstack/signal-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { CreateStreamDialog } from "../components/CreateStreamDialog"; import { seriesUsageRatio, seriesUsageTone } from "../lib/usage-tone"; diff --git a/core/notification-frontend/package.json b/core/notification-frontend/package.json index 720c1d774..88c8bc208 100644 --- a/core/notification-frontend/package.json +++ b/core/notification-frontend/package.json @@ -26,7 +26,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/notification-frontend/src/components/NotificationBell.tsx b/core/notification-frontend/src/components/NotificationBell.tsx index f3a6f0ca9..9023cd868 100644 --- a/core/notification-frontend/src/components/NotificationBell.tsx +++ b/core/notification-frontend/src/components/NotificationBell.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, type ReactNode } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Bell, CheckCheck } from "lucide-react"; import { Badge, diff --git a/core/notification-frontend/src/components/NotificationSubjects.tsx b/core/notification-frontend/src/components/NotificationSubjects.tsx index 757dc493e..855b89154 100644 --- a/core/notification-frontend/src/components/NotificationSubjects.tsx +++ b/core/notification-frontend/src/components/NotificationSubjects.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Popover, PopoverContent, diff --git a/core/notification-frontend/src/pages/NotificationSettingsPage.tsx b/core/notification-frontend/src/pages/NotificationSettingsPage.tsx index d5b97607d..2f2448100 100644 --- a/core/notification-frontend/src/pages/NotificationSettingsPage.tsx +++ b/core/notification-frontend/src/pages/NotificationSettingsPage.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Bell, Clock, Zap, Send, Activity } from "lucide-react"; import { PageLayout, diff --git a/core/notification-frontend/src/pages/NotificationsPage.tsx b/core/notification-frontend/src/pages/NotificationsPage.tsx index 1bbeb6369..9f145f6aa 100644 --- a/core/notification-frontend/src/pages/NotificationsPage.tsx +++ b/core/notification-frontend/src/pages/NotificationsPage.tsx @@ -1,5 +1,5 @@ import { useState, useCallback, useMemo } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Bell, Check, Trash2, ChevronDown, ChevronUp } from "lucide-react"; import { PageLayout, diff --git a/core/pluginmanager-frontend/package.json b/core/pluginmanager-frontend/package.json index f0f0c754c..8422cb23b 100644 --- a/core/pluginmanager-frontend/package.json +++ b/core/pluginmanager-frontend/package.json @@ -23,7 +23,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/pluginmanager-frontend/src/pages/InstallPluginPage.tsx b/core/pluginmanager-frontend/src/pages/InstallPluginPage.tsx index 5fa29c825..a2106d749 100644 --- a/core/pluginmanager-frontend/src/pages/InstallPluginPage.tsx +++ b/core/pluginmanager-frontend/src/pages/InstallPluginPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { PageLayout, Alert, diff --git a/core/pluginmanager-frontend/src/pages/InstalledPluginsPage.tsx b/core/pluginmanager-frontend/src/pages/InstalledPluginsPage.tsx index a3224ad05..869b2fe43 100644 --- a/core/pluginmanager-frontend/src/pages/InstalledPluginsPage.tsx +++ b/core/pluginmanager-frontend/src/pages/InstalledPluginsPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Puzzle, Trash2, Plus, History } from "lucide-react"; import { PageLayout, diff --git a/core/queue-frontend/package.json b/core/queue-frontend/package.json index b63978576..d396d1f19 100644 --- a/core/queue-frontend/package.json +++ b/core/queue-frontend/package.json @@ -34,7 +34,7 @@ "ajv-formats": "^3.0.1", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.0.0" }, "devDependencies": { diff --git a/core/queue-frontend/src/components/UserMenuItems.tsx b/core/queue-frontend/src/components/UserMenuItems.tsx index ba929906e..4e3a6fc6b 100644 --- a/core/queue-frontend/src/components/UserMenuItems.tsx +++ b/core/queue-frontend/src/components/UserMenuItems.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { ListOrdered } from "lucide-react"; import type { UserMenuItemsContext } from "@checkstack/frontend-api"; import { DropdownMenuItem } from "@checkstack/ui"; diff --git a/core/satellite-frontend/package.json b/core/satellite-frontend/package.json index c965cc8c7..e9df48179 100644 --- a/core/satellite-frontend/package.json +++ b/core/satellite-frontend/package.json @@ -26,7 +26,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@checkstack/tsconfig": "workspace:*", diff --git a/core/script-packages-frontend/package.json b/core/script-packages-frontend/package.json index 2592d71d8..d877bc740 100644 --- a/core/script-packages-frontend/package.json +++ b/core/script-packages-frontend/package.json @@ -33,7 +33,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@types/react": "^19.0.0", diff --git a/core/secrets-frontend/package.json b/core/secrets-frontend/package.json index 4e3bba834..9c4a94aa8 100644 --- a/core/secrets-frontend/package.json +++ b/core/secrets-frontend/package.json @@ -34,7 +34,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "@types/react": "^19.0.0", diff --git a/core/slo-frontend/package.json b/core/slo-frontend/package.json index 7ba4a6f28..62a982926 100644 --- a/core/slo-frontend/package.json +++ b/core/slo-frontend/package.json @@ -30,7 +30,7 @@ "date-fns": "^4.4.0", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "recharts": "^3.8.1" }, "devDependencies": { diff --git a/core/slo-frontend/src/components/SloObjectiveCard.tsx b/core/slo-frontend/src/components/SloObjectiveCard.tsx index 666860007..1c68213cd 100644 --- a/core/slo-frontend/src/components/SloObjectiveCard.tsx +++ b/core/slo-frontend/src/components/SloObjectiveCard.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { cn, formatPercent, pillToneStyles, StatusPill } from "@checkstack/ui"; import { ErrorBudgetBar } from "./ErrorBudgetBar"; import { BurnRateIndicator } from "./BurnRateIndicator"; diff --git a/core/slo-frontend/src/components/SystemSloPanel.tsx b/core/slo-frontend/src/components/SystemSloPanel.tsx index 067ddcf51..9035b61cf 100644 --- a/core/slo-frontend/src/components/SystemSloPanel.tsx +++ b/core/slo-frontend/src/components/SystemSloPanel.tsx @@ -8,7 +8,7 @@ import { ErrorBudgetBar } from "./ErrorBudgetBar"; import { BurnRateIndicator } from "./BurnRateIndicator"; import { DetailCard, formatPercent } from "@checkstack/ui"; import { Target } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; type Props = SlotContext; diff --git a/core/slo-frontend/src/pages/SloConfigPage.tsx b/core/slo-frontend/src/pages/SloConfigPage.tsx index 58f3e8157..05aa1ce38 100644 --- a/core/slo-frontend/src/pages/SloConfigPage.tsx +++ b/core/slo-frontend/src/pages/SloConfigPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { usePluginClient, accessApiRef, diff --git a/core/slo-frontend/src/pages/SloDetailPage.tsx b/core/slo-frontend/src/pages/SloDetailPage.tsx index 4d0526436..a33fcb51c 100644 --- a/core/slo-frontend/src/pages/SloDetailPage.tsx +++ b/core/slo-frontend/src/pages/SloDetailPage.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from "react"; -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; import { usePluginClient, wrapInSuspense } from "@checkstack/frontend-api"; import { SloApi } from "../api"; import { CatalogApi } from "@checkstack/catalog-common"; diff --git a/core/slo-frontend/src/pages/SloOverviewPage.tsx b/core/slo-frontend/src/pages/SloOverviewPage.tsx index 018beb336..887e9f814 100644 --- a/core/slo-frontend/src/pages/SloOverviewPage.tsx +++ b/core/slo-frontend/src/pages/SloOverviewPage.tsx @@ -12,7 +12,7 @@ import { QueryErrorState, } from "@checkstack/ui"; import { Target, ArrowRight } from "lucide-react"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { resolveRoute } from "@checkstack/common"; const SloOverviewPageContent: React.FC = () => { diff --git a/core/status-page-frontend/package.json b/core/status-page-frontend/package.json index 2df8b5fbc..c32b02623 100644 --- a/core/status-page-frontend/package.json +++ b/core/status-page-frontend/package.json @@ -24,7 +24,7 @@ "@checkstack/ui": "workspace:*", "lucide-react": "^1.17.0", "react": "19.2.7", - "react-router-dom": "^7.16.0" + "react-router": "^8.3.0" }, "devDependencies": { "typescript": "^5.0.0", diff --git a/core/status-page-frontend/src/pages/PublicEventDetailPage.tsx b/core/status-page-frontend/src/pages/PublicEventDetailPage.tsx index cafcfa537..e073cf45c 100644 --- a/core/status-page-frontend/src/pages/PublicEventDetailPage.tsx +++ b/core/status-page-frontend/src/pages/PublicEventDetailPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; import { EmptyState, LoadingSpinner, diff --git a/core/status-page-frontend/src/pages/PublicStatusPage.tsx b/core/status-page-frontend/src/pages/PublicStatusPage.tsx index ae8935ef9..f7bf84b2f 100644 --- a/core/status-page-frontend/src/pages/PublicStatusPage.tsx +++ b/core/status-page-frontend/src/pages/PublicStatusPage.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; import { EmptyState, LoadingSpinner, diff --git a/core/status-page-frontend/src/pages/StatusPageBuilderPage.tsx b/core/status-page-frontend/src/pages/StatusPageBuilderPage.tsx index 704b94fd3..c70ef906f 100644 --- a/core/status-page-frontend/src/pages/StatusPageBuilderPage.tsx +++ b/core/status-page-frontend/src/pages/StatusPageBuilderPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { PageLayout, Button, diff --git a/core/status-page-frontend/src/pages/StatusPagesListPage.tsx b/core/status-page-frontend/src/pages/StatusPagesListPage.tsx index 396fe4854..6ecd22d83 100644 --- a/core/status-page-frontend/src/pages/StatusPagesListPage.tsx +++ b/core/status-page-frontend/src/pages/StatusPagesListPage.tsx @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { PageLayout, Button, diff --git a/core/telemetry-frontend/package.json b/core/telemetry-frontend/package.json index 21603f092..db2014325 100644 --- a/core/telemetry-frontend/package.json +++ b/core/telemetry-frontend/package.json @@ -33,7 +33,7 @@ "lucide-react": "^0.469.0", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "zod": "^4.2.1" }, "devDependencies": { diff --git a/core/tracestream-frontend/package.json b/core/tracestream-frontend/package.json index 32791eaa6..fe401351e 100644 --- a/core/tracestream-frontend/package.json +++ b/core/tracestream-frontend/package.json @@ -43,7 +43,7 @@ "@tanstack/react-query": "^5.100.14", "react": "19.2.7", "react-dom": "19.2.7", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "lucide-react": "^0.469.0" }, "devDependencies": { diff --git a/core/tracestream-frontend/src/components/CreateStreamDialog.tsx b/core/tracestream-frontend/src/components/CreateStreamDialog.tsx index 7551a6e0c..3647bdee1 100644 --- a/core/tracestream-frontend/src/components/CreateStreamDialog.tsx +++ b/core/tracestream-frontend/src/components/CreateStreamDialog.tsx @@ -20,7 +20,7 @@ import { teamCreateErrorMessage, } from "@checkstack/auth-frontend"; import { TracestreamApi, tracestreamAccess } from "@checkstack/tracestream-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; export interface CreateStreamDialogProps { open: boolean; diff --git a/core/tracestream-frontend/src/components/DangerZoneSection.tsx b/core/tracestream-frontend/src/components/DangerZoneSection.tsx index 2b5f94e55..c0bebb31c 100644 --- a/core/tracestream-frontend/src/components/DangerZoneSection.tsx +++ b/core/tracestream-frontend/src/components/DangerZoneSection.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { Card, diff --git a/core/tracestream-frontend/src/components/ViewTraceLink.tsx b/core/tracestream-frontend/src/components/ViewTraceLink.tsx index 8a2d139f7..b9c74e667 100644 --- a/core/tracestream-frontend/src/components/ViewTraceLink.tsx +++ b/core/tracestream-frontend/src/components/ViewTraceLink.tsx @@ -1,4 +1,4 @@ -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { usePluginClient } from "@checkstack/frontend-api"; import { Button } from "@checkstack/ui"; import { Waypoints } from "lucide-react"; diff --git a/core/tracestream-frontend/src/pages/TraceStreamDetailPage.tsx b/core/tracestream-frontend/src/pages/TraceStreamDetailPage.tsx index eabeb2cfb..872647eb3 100644 --- a/core/tracestream-frontend/src/pages/TraceStreamDetailPage.tsx +++ b/core/tracestream-frontend/src/pages/TraceStreamDetailPage.tsx @@ -15,7 +15,7 @@ import { Radio, Settings, } from "lucide-react"; -import { useNavigate, useParams, useSearchParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router"; import { TracestreamApi } from "@checkstack/tracestream-common"; import { OverviewTab } from "../components/tabs/OverviewTab"; import { TracesTab } from "../components/tabs/TracesTab"; diff --git a/core/tracestream-frontend/src/pages/TraceStreamListPage.tsx b/core/tracestream-frontend/src/pages/TraceStreamListPage.tsx index 01542b05e..da531bd73 100644 --- a/core/tracestream-frontend/src/pages/TraceStreamListPage.tsx +++ b/core/tracestream-frontend/src/pages/TraceStreamListPage.tsx @@ -18,7 +18,7 @@ import { type TraceStream, type TraceStreamSummary, } from "@checkstack/tracestream-common"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { CreateStreamDialog } from "../components/CreateStreamDialog"; /** diff --git a/core/ui/.storybook/preview.tsx b/core/ui/.storybook/preview.tsx index 89f2f197a..8f240b16b 100644 --- a/core/ui/.storybook/preview.tsx +++ b/core/ui/.storybook/preview.tsx @@ -1,7 +1,7 @@ import type { Preview } from "@storybook/react"; import { withThemeByClassName } from "@storybook/addon-themes"; import React from "react"; -import { MemoryRouter } from "react-router-dom"; +import { MemoryRouter } from "react-router"; import { ApiRegistryBuilder, ApiProvider, diff --git a/core/ui/package.json b/core/ui/package.json index 6df4ee69c..3660fa1aa 100644 --- a/core/ui/package.json +++ b/core/ui/package.json @@ -47,7 +47,7 @@ "react-day-picker": "^9.13.0", "react-dom": "19.2.7", "react-markdown": "^10.1.0", - "react-router-dom": "^7.16.0", + "react-router": "^8.3.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.1", diff --git a/core/ui/src/components/NavItem.tsx b/core/ui/src/components/NavItem.tsx index 01f543af2..430c43129 100644 --- a/core/ui/src/components/NavItem.tsx +++ b/core/ui/src/components/NavItem.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect } from "react"; -import { NavLink, useLocation } from "react-router-dom"; +import { NavLink, useLocation } from "react-router"; import { ChevronDown } from "lucide-react"; import { cn, isNavRouteActive } from "../utils"; import { useApi, accessApiRef } from "@checkstack/frontend-api"; diff --git a/core/ui/src/components/NotFound.tsx b/core/ui/src/components/NotFound.tsx index 109676d3f..223bcbca8 100644 --- a/core/ui/src/components/NotFound.tsx +++ b/core/ui/src/components/NotFound.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useMemo } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { ArrowLeft, BookOpen, Command, Search } from "lucide-react"; import { APP_DOC_SLUGS, docsPath } from "@checkstack/common"; import { cn } from "../utils"; diff --git a/core/ui/src/hooks/useDataTableFilters.ts b/core/ui/src/hooks/useDataTableFilters.ts index d5fe751eb..aa5d1c2fc 100644 --- a/core/ui/src/hooks/useDataTableFilters.ts +++ b/core/ui/src/hooks/useDataTableFilters.ts @@ -1,5 +1,5 @@ import React from "react"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; import { EMPTY_TABLE_FILTERS, hasActiveTableFilters, diff --git a/docs/src/content/docs/developer-guide/backend/command-palette.md b/docs/src/content/docs/developer-guide/backend/command-palette.md index bfd089681..21c30e7d6 100644 --- a/docs/src/content/docs/developer-guide/backend/command-palette.md +++ b/docs/src/content/docs/developer-guide/backend/command-palette.md @@ -156,7 +156,7 @@ Users with the `"*"` (wildcard) access see all commands regardless of requiremen To support deep-linking from commands (e.g., "Create Incident" → opens create dialog), handle URL parameters in your frontend: ```tsx -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; function MyConfigPage() { const [searchParams, setSearchParams] = useSearchParams(); diff --git a/docs/src/content/docs/developer-guide/frontend/extension-points.md b/docs/src/content/docs/developer-guide/frontend/extension-points.md index 70d15f0f8..d4ab1a259 100644 --- a/docs/src/content/docs/developer-guide/frontend/extension-points.md +++ b/docs/src/content/docs/developer-guide/frontend/extension-points.md @@ -966,7 +966,7 @@ import type { UserMenuItemsContext } from "@checkstack/frontend-api"; import { qualifyAccessRuleId, resolveRoute } from "@checkstack/common"; import { access, pluginMetadata, myRoutes } from "@checkstack/myplugin-common"; import { DropdownMenuItem } from "@checkstack/ui"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; import { Settings } from "lucide-react"; export const MyPluginMenuItems = ({ diff --git a/docs/src/content/docs/developer-guide/frontend/master-detail.md b/docs/src/content/docs/developer-guide/frontend/master-detail.md index 62373d762..565b5c2ac 100644 --- a/docs/src/content/docs/developer-guide/frontend/master-detail.md +++ b/docs/src/content/docs/developer-guide/frontend/master-detail.md @@ -43,7 +43,7 @@ interface VirtualListProps { The selected item belongs in the route, not in component state, so deep links and back/forward work. Read the id from the params, and select by navigating - never by `setState`. ```tsx -import { useParams, useNavigate } from "react-router-dom"; +import { useParams, useNavigate } from "react-router"; import { resolveRoute } from "@checkstack/common"; import { healthcheckRoutes } from "@checkstack/healthcheck-common"; diff --git a/docs/src/content/docs/developer-guide/frontend/plugins.md b/docs/src/content/docs/developer-guide/frontend/plugins.md index f28f982f3..daa4f3689 100644 --- a/docs/src/content/docs/developer-guide/frontend/plugins.md +++ b/docs/src/content/docs/developer-guide/frontend/plugins.md @@ -534,7 +534,7 @@ extensions: [ ```typescript import { DropdownMenuItem } from "@checkstack/ui"; -import { Link } from "react-router-dom"; +import { Link } from "react-router"; export const MyUserMenuItems = () => { return ( @@ -552,7 +552,7 @@ export const MyUserMenuItems = () => { ### Navigation ```typescript -import { Link, useNavigate } from "react-router-dom"; +import { Link, useNavigate } from "react-router"; // Using Link View Item @@ -565,7 +565,7 @@ navigate("/items/123"); ### Route Parameters ```typescript -import { useParams } from "react-router-dom"; +import { useParams } from "react-router"; export const ItemDetailPage = () => { const { id } = useParams<{ id: string }>(); @@ -577,7 +577,7 @@ export const ItemDetailPage = () => { ### Query Parameters ```typescript -import { useSearchParams } from "react-router-dom"; +import { useSearchParams } from "react-router"; export const ItemListPage = () => { const [searchParams, setSearchParams] = useSearchParams();