From 966e51e8097a8d1849bd2be2e8afbd849b217741 Mon Sep 17 00:00:00 2001 From: enyineer Date: Sun, 26 Jul 2026 04:24:41 +0200 Subject: [PATCH] feat(frontend): migrate react-router-dom v7 to react-router v8 Resolves GHSA-qwww-vcr4-c8h2 (HIGH), which kept the dependency-graph security gate red on every PR. The advisory is an RSC-mode CSRF bypass; Checkstack runs a client-side SPA () and never uses RSC mode, so it was not exploitable here - but the fix ships only in the 8.x line, which the security auto-remediation deliberately refuses to reach (no major bumps), so it could never self-resolve. react-router-dom has no v8 (it was folded into react-router in v7), so this is a package swap, not a range bump: - 31 packages depend on react-router@^8.3.0 instead of react-router-dom@^7.16.0; 97 source files import from "react-router". - Module Federation host share + optimizeDeps + dedupe move to react-router (shared singleton requiredVersion ^8.0.0). Remotes never shared the router, so the remote contract is unchanged. - syncpack's unified-range group tracks react-router. - Docs code examples + the generated docs index updated. Every API Checkstack uses (BrowserRouter, MemoryRouter, Routes, Route, Link, NavLink, useLocation, useNavigate, useParams, useSearchParams) is exported by v8 unchanged, so no routing logic changed beyond the import specifier. v8 needs React >= 19.2.7, already pinned. Verified: typecheck, lint, syncpack, full unit suite (10,256 pass / 0 fail), frontend MF build, and a Trivy rescan confirming react-router is gone from the fixable set. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Hccsjzvqr6xfhGicNBjRgB --- .changeset/react-router-v8.md | 61 +++++++++++++++ .syncpackrc.json | 2 +- bun.lock | 76 +++++++++---------- core/about-frontend/package.json | 2 +- core/about-frontend/src/AboutMenuItem.tsx | 2 +- core/ai-backend/src/generated/docs-index.ts | 6 +- core/ai-frontend/package.json | 2 +- core/announcement-frontend/package.json | 2 +- core/announcement-frontend/vite.config.ts | 2 +- core/anomaly-frontend/package.json | 2 +- .../src/components/SystemAnomalyWidget.tsx | 2 +- core/api-docs-frontend/package.json | 2 +- core/auth-frontend/package.json | 2 +- .../src/components/AuthErrorPage.tsx | 2 +- .../src/components/AuthSettingsPage.tsx | 2 +- .../src/components/ChangePasswordPage.tsx | 2 +- .../src/components/ForgotPasswordPage.tsx | 2 +- .../src/components/LoginPage.tsx | 2 +- .../src/components/OAuthConsentPage.tsx | 2 +- .../src/components/OnboardingCheck.tsx | 2 +- .../src/components/OnboardingPage.tsx | 2 +- .../src/components/ProfilePage.tsx | 2 +- .../src/components/RegisterPage.tsx | 2 +- .../src/components/ResetPasswordPage.tsx | 2 +- core/automation-frontend/package.json | 2 +- .../components/RunAsServiceAccountPicker.tsx | 2 +- .../src/editor/action-leaf-cards.tsx | 2 +- .../src/pages/AutomationEditPage.tsx | 2 +- .../src/pages/AutomationListPage.tsx | 2 +- .../pages/AutomationTemplatePickerPage.tsx | 2 +- .../src/pages/RunDetailPage.tsx | 2 +- .../src/pages/RunsPage.tsx | 2 +- core/backend/src/index.ts | 2 +- core/catalog-frontend/package.json | 2 +- .../src/components/CatalogConfigPage.tsx | 2 +- .../src/components/CatalogPage.tsx | 2 +- .../src/components/LinkedStreamsCard.tsx | 2 +- .../src/components/SystemDetailPage.tsx | 2 +- .../src/components/SystemEditor.tsx | 2 +- .../components/browse/CatalogSystemRow.tsx | 2 +- .../src/hooks/useCatalogBrowseState.ts | 2 +- core/command-frontend/package.json | 2 +- .../src/components/SearchDialog.tsx | 2 +- core/dashboard-frontend/package.json | 2 +- .../DashboardSystemHealthSection.tsx | 2 +- .../components/DashboardWelcomeSection.tsx | 2 +- .../components/GettingStartedChecklist.tsx | 2 +- .../src/components/ProblemSystemCard.tsx | 2 +- core/dependency-frontend/package.json | 2 +- .../src/components/DependencyEditor.tsx | 2 +- .../components/SystemDependenciesPanel.tsx | 2 +- core/frontend/package.json | 2 +- core/frontend/src/App.tsx | 2 +- core/frontend/src/components/Sidebar.tsx | 2 +- core/frontend/src/public-app.tsx | 2 +- core/frontend/vite.config.ts | 8 +- core/gitops-frontend/package.json | 2 +- core/healthcheck-frontend/package.json | 2 +- .../src/components/HealthCheckDrawer.tsx | 2 +- .../components/HealthCheckSystemOverview.tsx | 2 +- .../SystemHealthCheckAssignment.tsx | 2 +- .../components/assignments/GeneralPanel.tsx | 2 +- .../src/pages/HealthCheckConfigPage.tsx | 4 +- .../pages/HealthCheckHistoryDetailPage.tsx | 2 +- .../src/pages/HealthCheckHistoryPage.tsx | 2 +- .../src/pages/HealthCheckIDEPage.tsx | 2 +- .../src/pages/StrategyPickerPage.tsx | 2 +- core/incident-frontend/package.json | 2 +- .../src/components/IncidentEditor.tsx | 2 +- .../src/components/SystemIncidentPanel.tsx | 2 +- .../src/pages/IncidentConfigPage.tsx | 2 +- .../src/pages/IncidentDetailPage.tsx | 2 +- .../src/pages/IncidentOverviewPage.tsx | 2 +- .../src/pages/SystemIncidentHistoryPage.tsx | 2 +- core/infrastructure-frontend/package.json | 2 +- .../src/pages/InfrastructureConfigPage.tsx | 2 +- core/integration-frontend/package.json | 2 +- .../src/pages/IntegrationsLandingPage.tsx | 2 +- .../src/pages/ProviderConnectionsPage.tsx | 2 +- core/logstream-frontend/package.json | 2 +- .../src/components/CorrelatedLogs.tsx | 2 +- .../src/components/CreateStreamDialog.tsx | 2 +- .../src/components/DangerZoneSection.tsx | 2 +- .../src/pages/LogStreamDetailPage.tsx | 2 +- .../src/pages/LogStreamListPage.tsx | 2 +- core/maintenance-frontend/package.json | 2 +- .../DashboardUpcomingMaintenances.tsx | 2 +- .../src/components/MaintenanceEditor.tsx | 2 +- .../src/components/SystemMaintenancePanel.tsx | 2 +- .../src/pages/MaintenanceConfigPage.tsx | 2 +- .../src/pages/MaintenanceDetailPage.tsx | 2 +- .../src/pages/MaintenanceOverviewPage.tsx | 2 +- .../pages/SystemMaintenanceHistoryPage.tsx | 2 +- core/metricstream-frontend/package.json | 2 +- .../src/components/CreateStreamDialog.tsx | 2 +- .../src/components/DangerZoneSection.tsx | 2 +- .../src/components/MetricQuickChart.tsx | 2 +- .../src/pages/MetricStreamDetailPage.tsx | 2 +- .../src/pages/MetricStreamListPage.tsx | 2 +- core/notification-frontend/package.json | 2 +- .../src/components/NotificationBell.tsx | 2 +- .../src/components/NotificationSubjects.tsx | 2 +- .../src/pages/NotificationSettingsPage.tsx | 2 +- .../src/pages/NotificationsPage.tsx | 2 +- core/pluginmanager-frontend/package.json | 2 +- .../src/pages/InstallPluginPage.tsx | 2 +- .../src/pages/InstalledPluginsPage.tsx | 2 +- core/queue-frontend/package.json | 2 +- .../src/components/UserMenuItems.tsx | 2 +- core/satellite-frontend/package.json | 2 +- core/script-packages-frontend/package.json | 2 +- core/secrets-frontend/package.json | 2 +- core/slo-frontend/package.json | 2 +- .../src/components/SloObjectiveCard.tsx | 2 +- .../src/components/SystemSloPanel.tsx | 2 +- core/slo-frontend/src/pages/SloConfigPage.tsx | 2 +- core/slo-frontend/src/pages/SloDetailPage.tsx | 2 +- .../src/pages/SloOverviewPage.tsx | 2 +- core/status-page-frontend/package.json | 2 +- .../src/pages/PublicEventDetailPage.tsx | 2 +- .../src/pages/PublicStatusPage.tsx | 2 +- .../src/pages/StatusPageBuilderPage.tsx | 2 +- .../src/pages/StatusPagesListPage.tsx | 2 +- core/telemetry-frontend/package.json | 2 +- core/tracestream-frontend/package.json | 2 +- .../src/components/CreateStreamDialog.tsx | 2 +- .../src/components/DangerZoneSection.tsx | 2 +- .../src/components/ViewTraceLink.tsx | 2 +- .../src/pages/TraceStreamDetailPage.tsx | 2 +- .../src/pages/TraceStreamListPage.tsx | 2 +- core/ui/.storybook/preview.tsx | 2 +- core/ui/package.json | 2 +- core/ui/src/components/NavItem.tsx | 2 +- core/ui/src/components/NotFound.tsx | 2 +- core/ui/src/hooks/useDataTableFilters.ts | 2 +- .../backend/command-palette.md | 2 +- .../frontend/extension-points.md | 2 +- .../developer-guide/frontend/master-detail.md | 2 +- .../docs/developer-guide/frontend/plugins.md | 8 +- 139 files changed, 244 insertions(+), 185 deletions(-) create mode 100644 .changeset/react-router-v8.md 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();