fix(console): the first-run setup exits land inside the console mount - #4186
Merged
Conversation
…#4181) SetupPage finished the first-run owner bootstrap with window.location.assign('/') at both of its exits — the success path after signUp() plus the bootstrap-org rename, and the already-signed-in bounce. location.assign bypasses React Router's basename, so on a console served under an injected `base href` (the framework CLI injects one for every embedded deployment) a root-relative '/' resolves to the ORIGIN root and drops a brand-new owner outside the SPA, on the first screen after creating their account. Both exits now route through withConsoleBase(). They stay FULL-PAGE navigations on purpose: ConsoleShell mounts MetadataProvider once auth resolves rather than once it authenticates (objectui#4042) and re-keys it on `language` alone, so the app list read while nobody was signed in would survive a router navigation and land the new owner in an appless console. withConsoleBase was module-private to LoginPage and had ALREADY been copied verbatim into RegisterPage, so the lift covers three call sites rather than the two the card assumed. LoginPage/RegisterPage behaviour is unchanged and pinned as unchanged across all three mount configurations. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 10, 2026 19:14
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4181
Premise, re-measured at this tip
Holds, both sites, unchanged in substance since filing (they sit at
:90and:149now rather than:88/:148):SetupPage.tsx:90— the already-signed-in bounce:window.location.assign('/')SetupPage.tsx:149— the success path, aftersignUp()and the bootstrap-org rename:window.location.assign('/')location.assigndoes not go through React Router, so itsbasenamenever applies and a root-relative/leaves the SPA. PR #4180 had already landed on main and deliberately did not touchSetupPage, so this branched off a tip that includes it; nothing needed rebasing.One correction to the filing, and it changes the shape of the fix: the helper was not only module-private to
LoginPage.RegisterPage.tsx:30already carried a byte-identical copy. The drift the card predicted as "next" had already happened, so the lift covers three call sites, not two.Why the exits stay full-page navigations
The card asked this first, so it was answered before anything was written — and the answer is that the reload is load-bearing.
withConsoleBase()fixes where the exits land, not what kind of navigation they are.ConsoleShell.ConnectedShellInnermountsMetadataProvideras soon as auth resolves — it deliberately does not gate onisAuthenticated(objectui#4042) — and keys it onlanguagealone. On a first-run deployment that means the metadata tree is already mounted and populated by reads that ran with no session, and nothing in its effect deps changes whensignUp()creates one. The landing resolution the exit hands off to (RootLandingRedirect.resolveLandingPath) reads that app list, so a routernavigate('/')would drop the brand-new owner into an appless console — a second dead end in place of the first.The permission grant compounds it: the bootstrap runs off a permission-grant middleware that "may land moments after
signUp()resolves" (SetupPage.handleSubmit's own comment), so even a re-fetch raced at exit time is not reliably the owner's world. Tearing the document down is what guarantees the console rebuilds with the session cookie present.It is also what the two sibling auth surfaces already do for the same reason —
LoginPageandRegisterPageboth exit throughwindow.location.assign(withConsoleBase(...)). So this keeps one idiom across all three, rather than inventing a fourth behaviour for the least-exercised page.The rationale is recorded on
POST_BOOTSTRAP_EXITin the source and pinned by a test, so the next reader does not have to re-derive it.The lift
withConsoleBase()moves toapps/console/src/utils/consoleBase.ts, byte-identical in body.LoginPage,RegisterPageandSetupPageall import it.While documenting it I checked something the card did not ask about but which decides whether this fix works at all: the helper reads
import.meta.env.BASE_URL, while the router reads the injectedbase href(App.tsx:resolveBasename). Two different sources — they agree for a reason rather than by construction, and that reason is now written down:/mount — Vite resolves a relativebaseto/in serve mode, so the prefix is a no-op and both spellings coincide. This is exactly why a standaloneos devrun can never reveal the bug.vite.config.tsbase: './', noVITE_BASE_PATH) — Vite 8 bakesBASE_URLas the literal./, so the helper returns a relative url.location.assignresolves that against the document base URL, which is precisely thebase hrefthe framework CLI injects — the same element the router reads. The two sources meet in the browser.VITE_BASE_PATH=/_console/) —BASE_URLis/_console/and the helper returns an absolute path.All three are now covered by tests, so the embedded case is asserted rather than assumed.
Tests
New:
apps/console/src/utils/consoleBase.test.ts(8) andapps/console/src/pages/auth/__tests__/authExitBasename.test.tsx(13).Landing is not asserted by string equality against the
assignargument — that would only restate the implementation, and it cannot express the embedded build at all, where the correct target is the relative./. Each assertion resolves the target against the document's base URL (the same resolutionlocation.assignperforms) and asks whether the result is inside the mount. The mount is configured the way a real deployment configures it: an injectedbase hrefplus theBASE_URLVite baked into that build.Beyond the two exits, the file pins that the success path still performs the org rename before exiting (the redirect change must not skip the work the page exists for), and that the exit remains a full-page navigation rather than a router one.
Reverse verification — direction predicted before running, then observed
Predicted: reverting only
SetupPage.tsxtoorigin/mainturns the four basename pins red, leaves the default-mount pins green, and leaves everyLoginPage/RegisterPagepin green — the last being what shows the lift is behaviour-neutral rather than merely untested.Observed, exactly that — 4 failed | 17 passed (21):
The four reds reproduce the filing's escape (
/instead of/_console/).consoleBase.test.tsstayed fully green — the helper was untouched by that revert — as did all sevenLoginPage/RegisterPagepins and both default-/-mount pins. Restored, re-run: 21/21 green.Local gates
Build closure (
--filter '@object-ui/console^...' build) was run first, before type-check.Changeset:
@object-ui/consolepatch — user-visible on deployed consoles.Generated by Claude Code