feat: adding in app specific rewrite valve to allow for browser router - #3522
Draft
themaherkhalil wants to merge 2 commits into
Draft
feat: adding in app specific rewrite valve to allow for browser router#3522themaherkhalil wants to merge 2 commits into
themaherkhalil wants to merge 2 commits into
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
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.
What
Switches the client and playground from
HashRoutertoBrowserRouter, so URLs read/app/<id>instead of/#/app/<id>. Runs against a stock Tomcat: nothing in Tomcat'sconf/is touched, and nomvnstep is needed for a local exploded deploy.Why this touches two layers
BrowserRouter needs two things hash routing did not:
.../dist/app/<id>/viewis not a real file, so Tomcat 404s it. The SPA shell has to be served instead.base: "./",index.htmlemits./assets/..., which the browser resolves against the current route rather than the app root. On a deep URL that fetches bundles from the wrong path and fails withExpected a JavaScript module script but the server responded with a MIME type of "text/html".Server side
META-INF/context.xmlenables Tomcat's built inRewriteValvefor this webapp only, with the rules inWEB-INF/rewrite.config. Both files live in the repo and both ship in the WAR (neither is in the pom's exclude lists), so no Tomcat level configuration is required. Rules are matched against the context relative URL, so the same file works at/semoss-uilocally and/SemossWebwhen deployed.The single rule serves
<pkg>/dist/index.htmlfor any path underpackages/<pkg>/dist/whose last segment has no file extension, which covers every package's SPA at once.Two notes for anyone editing that file later, both found the hard way:
%{REQUEST_FILENAME}and%{REQUEST_PATH}do not resolve usefully in a context level valve.-fand-dthrowIllegalArgumentException: The resource path [null] is not valid, and aRewriteCondon%{REQUEST_PATH}silently passes for every request, so asset URLs get answered withindex.html. The extension test therefore lives in the rule pattern as a negative lookahead, which is matched against the URL the valve actually rewrites.index.htmlcannot loop.Build
vite.config.tsin both apps takes its base fromVITE_BASE_URL, defaulting to/for the dev server.import.meta.env.BASE_URLis then the single source for both the asset base and the router basename, so the two cannot drift apart.Reviewers, please check this: the committed
.env.productionfiles assume the deployed context path is/SemossWeb. CI runs the samevite build --mode production, so if that is wrong for any environment the built assets will point at the wrong path. The alternative is dropping those files and settingVITE_BASE_URLas a CI variable instead, since shell env overrides.envfiles.For local work against an exploded checkout whose context path differs, add a gitignored override (
*.localis already ignored):pnpm run devis unaffected. Development mode does not read.env.production*, and Vite's dev server already does History API fallback.Shared route helpers
libs/shared/src/utility/router.tsadds two factories, following the existingcreateMcpPlatformUrlpattern rather than reading env inside shared. That matters becauselibs/renderermarks@semoss/sharedas external in its rollup build, so a build time env read inside shared could be frozen with the wrong value.createRouteHref(basename)for hrefs that cannot go through<Link>ornavigate(): anchors that open a new tab, andwindow.open.createSiblingAppHref(basename)for cross app links, deriving.../packages/<name>/dist/from the caller's own basename.Each app binds them once in
src/utility/router.ts. The playground's platform URL now comes fromcreateSiblingAppHref("client")instead ofVITE_PLATFORM_URL, leaving one path per deployment to maintain rather than two.Call sites converted
Anything that built a URL by hand had to change, because relative and hash hrefs no longer resolve correctly.
Client
share-overlaybuilt the share URL fromlocation.href.replace(location.hash, "#"), which prepends#to the entire URL once the hash is emptyhooks/useNavigatebuilt modified click new tab URLs asorigin + location.pathname + "#" + pathblocks-workspace-actionsstripped page ids by assigningwindow.location.href. That was a no reload hash change before and a full document reload after, which would have dropped the preview dialog that opens on the next lineproject-catalogandsettings-index-pageopened new tabs via`#${path}`platform-search-app,platform-search-engine,prompt-cardlanding-pagelinked to sibling apps with../../<pkg>/dist/, which only resolved because the landing page renders at the index routeRenderer (
libs/renderer)LinkBlockandstate.storeboth navigated between page blocks by assigningwindow.location.hash, which is a no-op under BrowserRouter. They now share a newresolveAppPagePathhelper.LinkBlockusesuseNavigate;StateStorecannot use hooks, so it takes an optionalnavigatesupplied byRendererand held in a ref, so routing to another page never re-triggers the app load effect. Design mode is unaffected, since it builds its store withmode: "static"and never enters that branch.Playground
room-input, both sites inagent-selector, and thesystem://resolver intools-view.Deliberately unchanged
libs/sdkembed-authstill falls back to reading the hash. It checkslocation.searchfirst, which is where the param now arrives, and the fallback keeps existing embed URLs working.global-nav-itemandmain-layoutstill parse#/...urls. Those read backend configured nav and embed items rather than building URLs, and both already convert to a router path, so existing configs keep working.auditlogstays onHashRouter, andterminalandbrowser-automationhave no router. None of them need an absolute base, because their document path never moves. Convertingauditloglater needs the same three changes the playground got here.App.tsxstill normalizes legacy#!/NCRT bookmarks. Those now land on the landing page rather than the deep route.Existing hash links degrade rather than break:
.../dist/#/app/<id>still loads the app and lands on the landing page, since the path matches the basename and the hash is ignored.Follow up needed outside this repo
GitHubAppClient.frontendUrl()in the Semoss backend hardcodes:Both the
#and the hardcoded context path need addressing. In the backend checkout I looked at, that method has no callers and the/github/install/appendpoint the client posts to is not present, so the flow is not currently reachable, but it needs a companion change before the GitHub App install callback lands correctly.Testing
Server side, verified against an exploded deploy at
webapps/semoss-ui:/semoss-ui/packages/client/dist/text/html/semoss-ui/packages/client/dist/app/<id>/viewtext/html(fallback)/semoss-ui/packages/client/dist/assets/<entry>.jstext/javascript/semoss-ui/packages/client/dist/assets/nope.js/semoss-ui/packages/client/dist/index.html/semoss-ui/packages/playground/dist/agent/<id>text/htmlIn the browser, after
pnpm run buildfrom the repo root so turbo rebuildslibs/renderer: hard refresh a deep route, open an app from the catalog, copy and open a share URL, Ctrl or Cmd click a nav button and a settings card, open a prompt in a new tab, follow an MCP usage link, hit Preview in the designer, and click a link block that moves between pages.Known gap
A route whose last segment contains a dot is not rewritten and 404s on refresh. No built in route can produce one, since every trailing param is an id, but a user defined page block route such as
report.v2would hit it. Closing it needs existence based fallback (a 404<error-page>, which only fires after Tomcat has confirmed the file is missing) rather than the extension test.🤖 Generated with Claude Code