Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/pseudo-route-segment-flags-3638.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@object-ui/app-shell': patch
---

Match the built-in pseudo-routes on whole path segments, so a mistyped app name can no longer render a different app (objectui#3638).

`AppContent` decides whether a URL is a built-in pseudo-route (`create-app`, `system/*`, `metadata/*`, `setup`) before it decides which app to render, and two of those switches were substring tests: `pathname.includes('/system')` and `pathname.includes('/metadata')`. Both are true for any segment that merely *starts* with the word — `system_log`, `system_setting`, `systems`, `metadata_import`, `metadata-export`. `isSpecialRoute` feeds `requestedAppMissing`, so visiting `/apps/<mistyped-app>/system_log` marked the URL as a pseudo-route, suppressed the "App not available" guard, fell back to the default app and rendered **that** app's shell with `system_log` taken as its object name — the exact "must NOT silently render a DIFFERENT app" case the fallback's own comment exists to prevent, with no indication that the requested app does not exist.

The two flags now test path *segments* (`pathname.split('/').includes('system' | 'metadata')`); `isCreateAppRoute`'s `endsWith('/create-app')` is unchanged. Every real pseudo-route spells the word as a whole segment — `system/marketplace{,/installed,/:packageId}`, the host's `system/{apps,profile,approvals,ai-approvals,audit-log,settings,objects,metadata/…}`, `metadata/{,_diagnostics,:type,…}` and the legacy `component/metadata/{directory,resource/*}` aliases — so all of them stay special, including in the zero-app branch that keys on these flags directly (objectui#3590 / #3610). Knock-on, in a zero-app console only: a `system`-prefixed near-miss such as `/apps/setup/system_log` now reaches the same "No Apps Configured" screen every other unresolved URL there reaches, instead of the pseudo-route branch's "Page not found".
21 changes: 17 additions & 4 deletions packages/app-shell/src/console/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,22 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
// Built-in pseudo-routes under /apps/* that are NOT metadata apps (create-app,
// system/*, metadata/*, setup). They must keep working — and may fall back to
// a default app — regardless of whether the segment resolves to an app.
// #3638 — `system` / `metadata` are matched as whole path SEGMENTS, not as
// substrings. `pathname.includes('/system')` was also true for any segment
// that merely STARTS with `system` (`system_log`, `system_setting`,
// `systems`), and likewise for `metadata` (`metadata_import`, …). That made
// `isSpecialRoute` true for an ordinary `/apps/:app/:objectName` URL whose
// object name happened to start that way — which suppressed
// `requestedAppMissing` below and let a MISTYPED app name silently render a
// DIFFERENT app (the exact failure the comment on the fallback describes).
// Every real pseudo-route spells them as full segments — `system/marketplace`,
// `system/metadata/:type`, `metadata/:type`, `component/metadata/resource` —
// so the segment test keeps all of them true (pinned in
// `__tests__/AppContent.pseudoRouteSegments.test.tsx`).
const pathSegments = location.pathname.split('/');
const isCreateAppRoute = location.pathname.endsWith('/create-app');
const isSystemRoute = location.pathname.includes('/system');
const isMetadataRoute = location.pathname.includes('/metadata');
const isSystemRoute = pathSegments.includes('system');
const isMetadataRoute = pathSegments.includes('metadata');
const isSetupRoute =
location.pathname === '/apps/setup' || location.pathname.startsWith('/apps/setup/');
const isSpecialRoute = isCreateAppRoute || isSystemRoute || isMetadataRoute || isSetupRoute;
Expand Down Expand Up @@ -637,8 +650,8 @@ export function AppContent({ extraRoutes, extraRoutesNoApp }: AppContentProps =
`sys-datasources` points straight at
`…/component/metadata/resource?type=datasource`, and `sys-objects`
arrives via the host's `system/metadata/:type` → same alias
rewrite. Both pass `isMetadataRoute` (a substring test on
`/metadata`) and so land in THIS branch, which declared no
rewrite. Both pass `isMetadataRoute` (a `metadata` path segment —
a substring test until #3638) and so land in THIS branch, which declared no
`component/…` route at all — every one of them rendered a blank
screen. Kept as a mirror rather than re-pointed navigation because
the alias already has exactly one canonical destination; adding a
Expand Down
Loading
Loading