From 7dbcb16ea789d1f9b0285c2d93c7fdbf3fc6eb57 Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Tue, 20 Jan 2026 13:13:05 -0500 Subject: [PATCH 1/4] update packages --- package-lock.json | 54 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5ebfa084..41b20b8d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,9 +60,9 @@ "@tanstack/eslint-plugin-router": "1.141.0", "@tanstack/react-form": "1.27.7", "@tanstack/react-query": "5.90.16", - "@tanstack/react-router": "1.145.7", + "@tanstack/react-router": "1.153.2", "@tanstack/react-virtual": "3.13.16", - "@tanstack/router-plugin": "1.145.7", + "@tanstack/router-plugin": "1.153.2", "@turf/bbox": "7.3.1", "@turf/bbox-polygon": "7.3.1", "@turf/center": "7.3.1", @@ -6016,9 +6016,9 @@ } }, "node_modules/@tanstack/history": { - "version": "1.145.7", - "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.145.7.tgz", - "integrity": "sha512-gMo/ReTUp0a3IOcZoI3hH6PLDC2R/5ELQ7P2yu9F6aEkA0wSQh+Q4qzMrtcKvF2ut0oE+16xWCGDo/TdYd6cEQ==", + "version": "1.153.2", + "resolved": "https://registry.npmjs.org/@tanstack/history/-/history-1.153.2.tgz", + "integrity": "sha512-TVa0Wju5w6JZGq/S74Q7TQNtKXDatJaB4NYrhMZVU9ETlkgpr35NhDfOzsCJ93P0KCo1ZoDodlFp3c54/dLsyw==", "dev": true, "license": "MIT", "engines": { @@ -6095,15 +6095,15 @@ } }, "node_modules/@tanstack/react-router": { - "version": "1.145.7", - "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.145.7.tgz", - "integrity": "sha512-0O+a4TjJSPXd2BsvDPwDPBKRQKYqNIBg5TAg9NzCteqJ0NXRxwohyqCksHqCEEtJe/uItwqmHoqkK4q5MDhEsA==", + "version": "1.153.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.153.2.tgz", + "integrity": "sha512-fAXUBA2gZAId7h2eSHsRcgTeF8pioUz8V5rrQ+IrvA0a6IsxhbTSKLYyqUg4jRDkkcUKtM8StKtvbZCY+0IYWw==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/history": "1.145.7", + "@tanstack/history": "1.153.2", "@tanstack/react-store": "^0.8.0", - "@tanstack/router-core": "1.145.7", + "@tanstack/router-core": "1.153.2", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" @@ -6169,13 +6169,13 @@ } }, "node_modules/@tanstack/router-core": { - "version": "1.145.7", - "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.145.7.tgz", - "integrity": "sha512-v6jx6JqVUBM0/FcBq1tX22xiPq8Ufc0PDEP582/4deYoq2/RYd+bZstANp3mGSsqdxE/luhoLYuuSQiwi/j1wA==", + "version": "1.153.2", + "resolved": "https://registry.npmjs.org/@tanstack/router-core/-/router-core-1.153.2.tgz", + "integrity": "sha512-WLaR+rSNW7bj9UCJQ3SKpuh6nZBZkpGnf2mpjn/uRB6joIQ3BU7aRdhb7w9Via/MP52iaHh5sd8NY3MaLpF2tQ==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/history": "1.145.7", + "@tanstack/history": "1.153.2", "@tanstack/store": "^0.8.0", "cookie-es": "^2.0.0", "seroval": "^1.4.1", @@ -6203,13 +6203,13 @@ } }, "node_modules/@tanstack/router-generator": { - "version": "1.145.7", - "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.145.7.tgz", - "integrity": "sha512-xg71c1WTku0ro0rgpJWh3Dt+ognV9qWe2KJHAPzrqfOYdUYu9sGq7Ri4jo8Rk0luXWZrWsrFdBP+9Jx6JH6zWA==", + "version": "1.153.2", + "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.153.2.tgz", + "integrity": "sha512-bEhmCtXq5vv3HukKq5zmTDBNDRqVllYxsHoWtqEvHv5hCb5xwKKfUMGemRoiQ96/wLFuGnA5DYkem2GZWcG3wg==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/router-core": "1.145.7", + "@tanstack/router-core": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "prettier": "^3.5.0", @@ -6247,9 +6247,9 @@ } }, "node_modules/@tanstack/router-plugin": { - "version": "1.145.7", - "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.145.7.tgz", - "integrity": "sha512-Rimo0NragYKHwjoYX9JBLS8VkZD4D/LqzzLIlX9yz93lmWFRu/DbuS7fDZNqX1Ea8naNvo18DlySszYLzC8XDg==", + "version": "1.153.2", + "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.153.2.tgz", + "integrity": "sha512-aMMc70ChM0wBYOToq39kTMKI2A0EKWpumiKTJyAwEglXf0raF48+26Fmv0gr9/5CLvD0g8ljllsskVDyzg8oDw==", "dev": true, "license": "MIT", "dependencies": { @@ -6259,8 +6259,8 @@ "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", - "@tanstack/router-core": "1.145.7", - "@tanstack/router-generator": "1.145.7", + "@tanstack/router-core": "1.153.2", + "@tanstack/router-generator": "1.153.2", "@tanstack/router-utils": "1.143.11", "@tanstack/virtual-file-routes": "1.145.4", "babel-dead-code-elimination": "^1.0.11", @@ -6277,7 +6277,7 @@ }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", - "@tanstack/react-router": "^1.145.7", + "@tanstack/react-router": "^1.153.2", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" @@ -9920,9 +9920,9 @@ } }, "node_modules/diff": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", - "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", "dev": true, "license": "BSD-3-Clause", "engines": { diff --git a/package.json b/package.json index f2f50c821..684b26637 100644 --- a/package.json +++ b/package.json @@ -119,9 +119,9 @@ "@tanstack/eslint-plugin-router": "1.141.0", "@tanstack/react-form": "1.27.7", "@tanstack/react-query": "5.90.16", - "@tanstack/react-router": "1.145.7", + "@tanstack/react-router": "1.153.2", "@tanstack/react-virtual": "3.13.16", - "@tanstack/router-plugin": "1.145.7", + "@tanstack/router-plugin": "1.153.2", "@turf/bbox": "7.3.1", "@turf/bbox-polygon": "7.3.1", "@turf/center": "7.3.1", From 103514a473a8a266d27de3de86a456e5146a99aa Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Tue, 20 Jan 2026 13:13:22 -0500 Subject: [PATCH 2/4] update route tree --- src/renderer/src/generated/routeTree.gen.ts | 42 +++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/renderer/src/generated/routeTree.gen.ts b/src/renderer/src/generated/routeTree.gen.ts index 914213bfa..5c4426ad1 100644 --- a/src/renderer/src/generated/routeTree.gen.ts +++ b/src/renderer/src/generated/routeTree.gen.ts @@ -286,7 +286,7 @@ export interface FileRoutesByFullPath { '/onboarding': typeof OnboardingRouteRouteWithChildren '/welcome': typeof WelcomeRoute '/app/settings': typeof AppSettingsRouteRouteWithChildren - '/onboarding/project': typeof OnboardingProjectIndexRoute + '/onboarding/project': typeof OnboardingProjectRouteRouteWithChildren '/onboarding/data-and-privacy': typeof OnboardingDataAndPrivacyRoute '/onboarding/device-name': typeof OnboardingDeviceNameRoute '/onboarding/privacy-policy': typeof OnboardingPrivacyPolicyRoute @@ -297,26 +297,27 @@ export interface FileRoutesByFullPath { '/app/settings/language': typeof AppSettingsLanguageRoute '/app/settings/test-data': typeof AppSettingsTestDataRoute '/app/settings/': typeof AppSettingsIndexRoute + '/onboarding/project/': typeof OnboardingProjectIndexRoute '/app/projects/$projectId/invite': typeof AppProjectsProjectIdInviteRouteRouteWithChildren '/app/projects/$projectId/settings': typeof AppProjectsProjectIdSettingsRouteRouteWithChildren '/app/projects/$projectId/team': typeof AppProjectsProjectIdTeamRouteRouteWithChildren '/app/projects/$projectId/download': typeof AppProjectsProjectIdDownloadRoute '/app/projects/$projectId/': typeof AppProjectsProjectIdIndexRoute - '/onboarding/project/create': typeof OnboardingProjectCreateIndexRoute - '/onboarding/project/join': typeof OnboardingProjectJoinIndexRoute + '/onboarding/project/create/': typeof OnboardingProjectCreateIndexRoute + '/onboarding/project/join/': typeof OnboardingProjectJoinIndexRoute '/app/projects/$projectId/settings/categories': typeof AppProjectsProjectIdSettingsCategoriesRoute '/app/projects/$projectId/settings/info': typeof AppProjectsProjectIdSettingsInfoRoute '/app/projects/$projectId/team/$deviceId': typeof AppProjectsProjectIdTeamDeviceIdRoute '/onboarding/project/create/$projectId/success': typeof OnboardingProjectCreateProjectIdSuccessRoute '/onboarding/project/join/$inviteId/success': typeof OnboardingProjectJoinInviteIdSuccessRoute - '/app/projects/$projectId/exchange': typeof AppProjectsProjectIdExchangeIndexRoute + '/app/projects/$projectId/exchange/': typeof AppProjectsProjectIdExchangeIndexRoute '/app/projects/$projectId/invite/': typeof AppProjectsProjectIdInviteIndexRoute '/app/projects/$projectId/settings/': typeof AppProjectsProjectIdSettingsIndexRoute '/app/projects/$projectId/team/': typeof AppProjectsProjectIdTeamIndexRoute '/app/projects/$projectId/invite/devices/$deviceId': typeof AppProjectsProjectIdInviteDevicesDeviceIdRouteRouteWithChildren - '/app/projects/$projectId/observations/$observationDocId': typeof AppProjectsProjectIdObservationsObservationDocIdIndexRoute - '/app/projects/$projectId/tracks/$trackDocId': typeof AppProjectsProjectIdTracksTrackDocIdIndexRoute - '/app/projects/$projectId/invite/devices': typeof AppProjectsProjectIdInviteDevicesIndexRoute + '/app/projects/$projectId/observations/$observationDocId/': typeof AppProjectsProjectIdObservationsObservationDocIdIndexRoute + '/app/projects/$projectId/tracks/$trackDocId/': typeof AppProjectsProjectIdTracksTrackDocIdIndexRoute + '/app/projects/$projectId/invite/devices/': typeof AppProjectsProjectIdInviteDevicesIndexRoute '/app/projects/$projectId/invite/devices/$deviceId/role': typeof AppProjectsProjectIdInviteDevicesDeviceIdRoleRoute '/app/projects/$projectId/invite/devices/$deviceId/send': typeof AppProjectsProjectIdInviteDevicesDeviceIdSendRoute '/app/projects/$projectId/observations/$observationDocId/attachments/$driveId/$type/$variant/$name': typeof AppProjectsProjectIdObservationsObservationDocIdAttachmentsDriveIdTypeVariantNameRoute @@ -419,26 +420,27 @@ export interface FileRouteTypes { | '/app/settings/language' | '/app/settings/test-data' | '/app/settings/' + | '/onboarding/project/' | '/app/projects/$projectId/invite' | '/app/projects/$projectId/settings' | '/app/projects/$projectId/team' | '/app/projects/$projectId/download' | '/app/projects/$projectId/' - | '/onboarding/project/create' - | '/onboarding/project/join' + | '/onboarding/project/create/' + | '/onboarding/project/join/' | '/app/projects/$projectId/settings/categories' | '/app/projects/$projectId/settings/info' | '/app/projects/$projectId/team/$deviceId' | '/onboarding/project/create/$projectId/success' | '/onboarding/project/join/$inviteId/success' - | '/app/projects/$projectId/exchange' + | '/app/projects/$projectId/exchange/' | '/app/projects/$projectId/invite/' | '/app/projects/$projectId/settings/' | '/app/projects/$projectId/team/' | '/app/projects/$projectId/invite/devices/$deviceId' - | '/app/projects/$projectId/observations/$observationDocId' - | '/app/projects/$projectId/tracks/$trackDocId' - | '/app/projects/$projectId/invite/devices' + | '/app/projects/$projectId/observations/$observationDocId/' + | '/app/projects/$projectId/tracks/$trackDocId/' + | '/app/projects/$projectId/invite/devices/' | '/app/projects/$projectId/invite/devices/$deviceId/role' | '/app/projects/$projectId/invite/devices/$deviceId/send' | '/app/projects/$projectId/observations/$observationDocId/attachments/$driveId/$type/$variant/$name' @@ -597,7 +599,7 @@ declare module '@tanstack/react-router' { '/onboarding/project_/': { id: '/onboarding/project_/' path: '/project' - fullPath: '/onboarding/project' + fullPath: '/onboarding/project/' preLoaderRoute: typeof OnboardingProjectIndexRouteImport parentRoute: typeof OnboardingRouteRoute } @@ -653,14 +655,14 @@ declare module '@tanstack/react-router' { '/onboarding/project/join/': { id: '/onboarding/project/join/' path: '/join' - fullPath: '/onboarding/project/join' + fullPath: '/onboarding/project/join/' preLoaderRoute: typeof OnboardingProjectJoinIndexRouteImport parentRoute: typeof OnboardingProjectRouteRoute } '/onboarding/project/create/': { id: '/onboarding/project/create/' path: '/create' - fullPath: '/onboarding/project/create' + fullPath: '/onboarding/project/create/' preLoaderRoute: typeof OnboardingProjectCreateIndexRouteImport parentRoute: typeof OnboardingProjectRouteRoute } @@ -723,7 +725,7 @@ declare module '@tanstack/react-router' { '/app/projects/$projectId_/exchange/': { id: '/app/projects/$projectId_/exchange/' path: '/projects/$projectId/exchange' - fullPath: '/app/projects/$projectId/exchange' + fullPath: '/app/projects/$projectId/exchange/' preLoaderRoute: typeof AppProjectsProjectIdExchangeIndexRouteImport parentRoute: typeof AppRouteRoute } @@ -765,21 +767,21 @@ declare module '@tanstack/react-router' { '/app/projects/$projectId_/invite/devices/': { id: '/app/projects/$projectId_/invite/devices/' path: '/devices' - fullPath: '/app/projects/$projectId/invite/devices' + fullPath: '/app/projects/$projectId/invite/devices/' preLoaderRoute: typeof AppProjectsProjectIdInviteDevicesIndexRouteImport parentRoute: typeof AppProjectsProjectIdInviteRouteRoute } '/app/projects/$projectId/tracks/$trackDocId/': { id: '/app/projects/$projectId/tracks/$trackDocId/' path: '/tracks/$trackDocId' - fullPath: '/app/projects/$projectId/tracks/$trackDocId' + fullPath: '/app/projects/$projectId/tracks/$trackDocId/' preLoaderRoute: typeof AppProjectsProjectIdTracksTrackDocIdIndexRouteImport parentRoute: typeof AppProjectsProjectIdRouteRoute } '/app/projects/$projectId/observations/$observationDocId/': { id: '/app/projects/$projectId/observations/$observationDocId/' path: '/observations/$observationDocId' - fullPath: '/app/projects/$projectId/observations/$observationDocId' + fullPath: '/app/projects/$projectId/observations/$observationDocId/' preLoaderRoute: typeof AppProjectsProjectIdObservationsObservationDocIdIndexRouteImport parentRoute: typeof AppProjectsProjectIdRouteRoute } From 727ff711c5c24fdbb23c8d5bb83c46004c31a7b6 Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Tue, 20 Jan 2026 13:14:07 -0500 Subject: [PATCH 3/4] use Route.redirect --- .../attachments/$driveId.$type.$variant.$name.tsx | 4 ++-- .../$projectId_/invite/devices/$deviceId/route.tsx | 6 +++--- .../routes/app/projects/$projectId_/invite/index.tsx | 4 ++-- .../routes/app/projects/$projectId_/invite/route.tsx | 9 ++------- .../app/projects/$projectId_/settings/route.tsx | 9 ++------- src/renderer/src/routes/app/route.tsx | 5 ++--- src/renderer/src/routes/app/settings_/test-data.tsx | 4 ++-- src/renderer/src/routes/index.tsx | 12 ++++++------ .../onboarding/project/create/$projectId.success.tsx | 5 ++--- .../onboarding/project/join/$inviteId/success.tsx | 4 ++-- src/renderer/src/routes/onboarding/project/route.tsx | 3 +-- src/renderer/src/routes/onboarding/route.tsx | 6 +++--- 12 files changed, 29 insertions(+), 42 deletions(-) diff --git a/src/renderer/src/routes/app/projects/$projectId/observations/$observationDocId/attachments/$driveId.$type.$variant.$name.tsx b/src/renderer/src/routes/app/projects/$projectId/observations/$observationDocId/attachments/$driveId.$type.$variant.$name.tsx index 6952e82ea..003d3d096 100644 --- a/src/renderer/src/routes/app/projects/$projectId/observations/$observationDocId/attachments/$driveId.$type.$variant.$name.tsx +++ b/src/renderer/src/routes/app/projects/$projectId/observations/$observationDocId/attachments/$driveId.$type.$variant.$name.tsx @@ -18,7 +18,7 @@ import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' import { captureMessage } from '@sentry/react' import { useMutation, useSuspenseQuery } from '@tanstack/react-query' -import { createFileRoute, redirect, useRouter } from '@tanstack/react-router' +import { createFileRoute, useRouter } from '@tanstack/react-router' import { defineMessages, useIntl } from 'react-intl' import * as v from 'valibot' @@ -104,7 +104,7 @@ export const Route = createFileRoute( ) }) ) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId/observations/$observationDocId', params: { projectId, observationDocId }, }) diff --git a/src/renderer/src/routes/app/projects/$projectId_/invite/devices/$deviceId/route.tsx b/src/renderer/src/routes/app/projects/$projectId_/invite/devices/$deviceId/route.tsx index 44384779e..0bb54cfd0 100644 --- a/src/renderer/src/routes/app/projects/$projectId_/invite/devices/$deviceId/route.tsx +++ b/src/renderer/src/routes/app/projects/$projectId_/invite/devices/$deviceId/route.tsx @@ -1,4 +1,4 @@ -import { Outlet, createFileRoute, redirect } from '@tanstack/react-router' +import { Outlet, createFileRoute } from '@tanstack/react-router' import { GenericRoutePendingComponent } from '../../../../../../../components/generic-route-pending-component' @@ -15,7 +15,7 @@ export const Route = createFileRoute( const matchingPeer = peers.find((p) => p.deviceId === deviceId) if (!matchingPeer) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId/invite/devices', params: { projectId }, replace: true, @@ -31,7 +31,7 @@ export const Route = createFileRoute( // TODO: Do not redirect if member left project? if (member) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId/invite/devices', params: { projectId }, replace: true, diff --git a/src/renderer/src/routes/app/projects/$projectId_/invite/index.tsx b/src/renderer/src/routes/app/projects/$projectId_/invite/index.tsx index 171fcb3f1..802ae5fa1 100644 --- a/src/renderer/src/routes/app/projects/$projectId_/invite/index.tsx +++ b/src/renderer/src/routes/app/projects/$projectId_/invite/index.tsx @@ -4,7 +4,7 @@ import List from '@mui/material/List' import ListItem from '@mui/material/ListItem' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' -import { createFileRoute, redirect, useRouter } from '@tanstack/react-router' +import { createFileRoute, useRouter } from '@tanstack/react-router' import { defineMessages, useIntl } from 'react-intl' import { BLUE_GREY, DARK_GREY, DARK_ORANGE } from '../../../../../colors' @@ -31,7 +31,7 @@ export const Route = createFileRoute('/app/projects/$projectId_/invite/')({ }) if (members.length > 1) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId/invite/devices', params: { projectId }, replace: true, diff --git a/src/renderer/src/routes/app/projects/$projectId_/invite/route.tsx b/src/renderer/src/routes/app/projects/$projectId_/invite/route.tsx index d160742b2..86755b46b 100644 --- a/src/renderer/src/routes/app/projects/$projectId_/invite/route.tsx +++ b/src/renderer/src/routes/app/projects/$projectId_/invite/route.tsx @@ -1,10 +1,5 @@ import Box from '@mui/material/Box' -import { - Outlet, - createFileRoute, - notFound, - redirect, -} from '@tanstack/react-router' +import { Outlet, createFileRoute, notFound } from '@tanstack/react-router' import { TwoPanelLayout } from '../../../-components/two-panel-layout' import { LIGHT_GREY } from '../../../../../colors' @@ -52,7 +47,7 @@ export const Route = createFileRoute('/app/projects/$projectId_/invite')({ // TODO: Should be checking the specific role permissions instead // https://github.com/digidem/comapeo-mobile/issues/1316 if (!isAtLeastCoordinator) { - throw redirect({ to: '/', replace: true }) + throw Route.redirect({ to: '/', replace: true }) } return { projectApi } diff --git a/src/renderer/src/routes/app/projects/$projectId_/settings/route.tsx b/src/renderer/src/routes/app/projects/$projectId_/settings/route.tsx index b80649779..105847449 100644 --- a/src/renderer/src/routes/app/projects/$projectId_/settings/route.tsx +++ b/src/renderer/src/routes/app/projects/$projectId_/settings/route.tsx @@ -1,10 +1,5 @@ import Box from '@mui/material/Box' -import { - Outlet, - createFileRoute, - notFound, - redirect, -} from '@tanstack/react-router' +import { Outlet, createFileRoute, notFound } from '@tanstack/react-router' import { TwoPanelLayout } from '../../../-components/two-panel-layout' import { LIGHT_GREY } from '../../../../../colors' @@ -51,7 +46,7 @@ export const Route = createFileRoute('/app/projects/$projectId_/settings')({ // TODO: Should be checking the specific role permissions instead // https://github.com/digidem/comapeo-mobile/issues/1316 if (!isAtLeastCoordinator) { - throw redirect({ to: '/', replace: true }) + throw Route.redirect({ to: '/', replace: true }) } return { projectApi } diff --git a/src/renderer/src/routes/app/route.tsx b/src/renderer/src/routes/app/route.tsx index 97b630a11..9646e3dde 100644 --- a/src/renderer/src/routes/app/route.tsx +++ b/src/renderer/src/routes/app/route.tsx @@ -7,7 +7,6 @@ import { useIsMutating } from '@tanstack/react-query' import { Outlet, createFileRoute, - redirect, useChildMatches, } from '@tanstack/react-router' import { defineMessages, useIntl } from 'react-intl' @@ -86,7 +85,7 @@ export const Route = createFileRoute('/app')({ ) { activeProjectIdStore.actions.update(undefined) - throw redirect({ + throw Route.redirect({ // TODO: Kind of hacky but no way to access router instance here at the moment href: buildDocumentReloadURL( { @@ -102,7 +101,7 @@ export const Route = createFileRoute('/app')({ // Redirect to project-specific "initial" page if the current route is the just /app if (matches.at(-1)!.fullPath === '/app') { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId', params: { projectId: activeProjectId }, replace: true, diff --git a/src/renderer/src/routes/app/settings_/test-data.tsx b/src/renderer/src/routes/app/settings_/test-data.tsx index 29ed3d1d5..2e8ce5bd3 100644 --- a/src/renderer/src/routes/app/settings_/test-data.tsx +++ b/src/renderer/src/routes/app/settings_/test-data.tsx @@ -23,7 +23,7 @@ import TextField from '@mui/material/TextField' import Typography from '@mui/material/Typography' import { useStore } from '@tanstack/react-form' import { useMutation, useSuspenseQuery } from '@tanstack/react-query' -import { createFileRoute, redirect, useRouter } from '@tanstack/react-router' +import { createFileRoute, useRouter } from '@tanstack/react-router' import { bboxPolygon } from '@turf/bbox-polygon' import { featureCollection, lengthToDegrees } from '@turf/helpers' import { randomPosition } from '@turf/random' @@ -54,7 +54,7 @@ export const Route = createFileRoute('/app/settings_/test-data')({ __APP_TYPE__ === 'production' || import.meta.env.VITE_FEATURE_TEST_DATA_UI !== 'true' ) { - throw redirect({ to: '/', replace: true }) + throw Route.redirect({ to: '/', replace: true }) } }, loader: async ({ context }) => { diff --git a/src/renderer/src/routes/index.tsx b/src/renderer/src/routes/index.tsx index bba05cd2c..3615f1908 100644 --- a/src/renderer/src/routes/index.tsx +++ b/src/renderer/src/routes/index.tsx @@ -1,4 +1,4 @@ -import { createFileRoute, redirect } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' import { COMAPEO_CORE_REACT_ROOT_QUERY_KEY } from '../lib/comapeo' @@ -15,13 +15,13 @@ export const Route = createFileRoute('/')({ // NOTE: Implicit check that the user hasn't completed the onboarding yet. if (!ownDeviceInfo.name) { - throw redirect({ to: '/welcome', replace: true }) + throw Route.redirect({ to: '/welcome', replace: true }) } const activeProjectId = activeProjectIdStore.instance.getState() if (activeProjectId) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId', params: { projectId: activeProjectId }, replace: true, @@ -33,7 +33,7 @@ export const Route = createFileRoute('/')({ // The better solution is probably a project selection page of some sort, // as opposed to automatic redirection to a valid project. - const projects = await queryClient.ensureQueryData({ + const projects = await queryClient.fetchQuery({ queryKey: [COMAPEO_CORE_REACT_ROOT_QUERY_KEY, 'projects'], queryFn: async () => { return clientApi.listProjects() @@ -43,14 +43,14 @@ export const Route = createFileRoute('/')({ const projectToUse = projects[0] if (projectToUse) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId', params: { projectId: projectToUse.projectId }, replace: true, }) } - throw redirect({ + throw Route.redirect({ to: '/onboarding/project', replace: true, }) diff --git a/src/renderer/src/routes/onboarding/project/create/$projectId.success.tsx b/src/renderer/src/routes/onboarding/project/create/$projectId.success.tsx index 9f77b6ca0..123832848 100644 --- a/src/renderer/src/routes/onboarding/project/create/$projectId.success.tsx +++ b/src/renderer/src/routes/onboarding/project/create/$projectId.success.tsx @@ -6,7 +6,6 @@ import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' import { createFileRoute, - redirect, useRouter, type NotFoundRouteProps, } from '@tanstack/react-router' @@ -34,7 +33,7 @@ export const Route = createFileRoute( }) } catch { // We are assuming that getting the project fails because it does not exist - throw redirect({ to: '/onboarding/project/create' }) + throw Route.redirect({ to: '/onboarding/project/create' }) } const settings = await queryClient.ensureQueryData({ @@ -52,7 +51,7 @@ export const Route = createFileRoute( // TODO: Kind of hacky but we do not allow creating a project without a name // so it should be okay to redirect them to the project creation page for now. if (!settings.name) { - throw redirect({ to: '/onboarding/project/create' }) + throw Route.redirect({ to: '/onboarding/project/create' }) } activeProjectIdStore.actions.update(params.projectId) diff --git a/src/renderer/src/routes/onboarding/project/join/$inviteId/success.tsx b/src/renderer/src/routes/onboarding/project/join/$inviteId/success.tsx index 91a77c6b0..d7658f714 100644 --- a/src/renderer/src/routes/onboarding/project/join/$inviteId/success.tsx +++ b/src/renderer/src/routes/onboarding/project/join/$inviteId/success.tsx @@ -4,7 +4,7 @@ import Box from '@mui/material/Box' import Container from '@mui/material/Container' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' -import { createFileRoute, redirect } from '@tanstack/react-router' +import { createFileRoute } from '@tanstack/react-router' import { defineMessages, useIntl } from 'react-intl' import * as v from 'valibot' @@ -53,7 +53,7 @@ export const Route = createFileRoute( // Redirect if the invite response did not actually succeed if (invite.state !== 'joined') { - throw redirect({ + throw Route.redirect({ to: '/onboarding/project', replace: true, }) diff --git a/src/renderer/src/routes/onboarding/project/route.tsx b/src/renderer/src/routes/onboarding/project/route.tsx index dcb1166df..6ec35cd54 100644 --- a/src/renderer/src/routes/onboarding/project/route.tsx +++ b/src/renderer/src/routes/onboarding/project/route.tsx @@ -2,7 +2,6 @@ import { useIsMutating } from '@tanstack/react-query' import { Outlet, createFileRoute, - redirect, useChildMatches, useRouter, } from '@tanstack/react-router' @@ -23,7 +22,7 @@ export const Route = createFileRoute('/onboarding/project')({ }) if (!ownDeviceInfo.name) { - throw redirect({ + throw Route.redirect({ to: '/onboarding/device-name', replace: true, }) diff --git a/src/renderer/src/routes/onboarding/route.tsx b/src/renderer/src/routes/onboarding/route.tsx index 009c85230..ee5693e5e 100644 --- a/src/renderer/src/routes/onboarding/route.tsx +++ b/src/renderer/src/routes/onboarding/route.tsx @@ -1,4 +1,4 @@ -import { Outlet, createFileRoute, redirect } from '@tanstack/react-router' +import { Outlet, createFileRoute } from '@tanstack/react-router' import { COMAPEO_CORE_REACT_ROOT_QUERY_KEY } from '../../lib/comapeo' @@ -9,7 +9,7 @@ export const Route = createFileRoute('/onboarding')({ const activeProjectId = activeProjectIdStore.instance.getState() if (activeProjectId) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId', params: { projectId: activeProjectId }, replace: true, @@ -31,7 +31,7 @@ export const Route = createFileRoute('/onboarding')({ const projectToUse = projects[0] if (projectToUse) { - throw redirect({ + throw Route.redirect({ to: '/app/projects/$projectId', params: { projectId: projectToUse.projectId }, replace: true, From 32d6e400f0f6e03cfe61674f67c090b6b3c99f62 Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Tue, 20 Jan 2026 13:21:49 -0500 Subject: [PATCH 4/4] add patch for safe protocols check in router core --- ...2+001+define-custom-protocol-as-safe.patch | 26 +++++++++++++++++++ patches/README.md | 6 +++++ 2 files changed, 32 insertions(+) create mode 100644 patches/@tanstack+router-core+1.153.2+001+define-custom-protocol-as-safe.patch diff --git a/patches/@tanstack+router-core+1.153.2+001+define-custom-protocol-as-safe.patch b/patches/@tanstack+router-core+1.153.2+001+define-custom-protocol-as-safe.patch new file mode 100644 index 000000000..38f1f1e30 --- /dev/null +++ b/patches/@tanstack+router-core+1.153.2+001+define-custom-protocol-as-safe.patch @@ -0,0 +1,26 @@ +diff --git a/node_modules/@tanstack/router-core/dist/cjs/utils.cjs b/node_modules/@tanstack/router-core/dist/cjs/utils.cjs +index 346cac9..f364a0e 100644 +--- a/node_modules/@tanstack/router-core/dist/cjs/utils.cjs ++++ b/node_modules/@tanstack/router-core/dist/cjs/utils.cjs +@@ -182,7 +182,7 @@ function decodeSegment(segment) { + } + return sanitizePathSegment(decoded); + } +-const SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:"]; ++const SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "comapeo:"]; + function isDangerousProtocol(url) { + if (!url) return false; + try { +diff --git a/node_modules/@tanstack/router-core/dist/esm/utils.js b/node_modules/@tanstack/router-core/dist/esm/utils.js +index 20822c1..c4b9e50 100644 +--- a/node_modules/@tanstack/router-core/dist/esm/utils.js ++++ b/node_modules/@tanstack/router-core/dist/esm/utils.js +@@ -180,7 +180,7 @@ function decodeSegment(segment) { + } + return sanitizePathSegment(decoded); + } +-const SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:"]; ++const SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "comapeo:"]; + function isDangerousProtocol(url) { + if (!url) return false; + try { diff --git a/patches/README.md b/patches/README.md index dc8b693c9..c56fa4e2b 100644 --- a/patches/README.md +++ b/patches/README.md @@ -8,3 +8,9 @@ fixes. ### [Do not watch fallback map patch when setting up SMP server plugin](./@comapeo+core+5.2.1+001+fix-smp-fallback-map-setup.patch) By default, core sets up a file watcher for the `fallbackMapPath` option that's provided when instantiating `MapeoManager`. This does not work when packaging the app as an ASAR file (via Electron Forge) because watching a file within the ASAR directory is not possible. Instead, we change the setup so that it does not try to watch the file and instead make the assumption that the file always exists on instantiation, which is generally the case in CoMapeo Desktop (for now). + +## @tanstack/router-core + +### [Define `comapeo:` protocol as safe](./@tanstack+router-core+1.153.2+001+define-custom-protocol-as-safe.patch) + +When the router attempts to do a full page reload, it checks the protocol for security purposes. It uses a hardcoded list for known "safe" protocols which results in issues in the app that attempt to navigate to a `comapeo:` URL with `reloadDocument: true` set. This patch updates the hardcoded list to account for our custom protocol so that the router doesn't consider it dangerous. We are responsible for doing safety checks related to accessing resources using this protocol, which is handled in the protocol handler that's implemented in the main process.