Commit b13e5bd
authored
UN-3185: Defer heavy chunks on login route + cache fingerprinted assets (#2114)
* UN-3185: Defer heavy chunks on login route + cache fingerprinted assets
Fix 1 (nginx.conf): serve content-hashed /assets/* with a 1y immutable
Cache-Control while keeping index.html and config/runtime-config.js
non-cached, so repeat visits stop re-fetching the whole bundle.
Fix 2: stop the unauthenticated /landing page from eagerly downloading
the full app. Convert OSS route pages and enterprise plugin route
elements to React.lazy behind a single <Suspense>, and lazy-load the
app shell (PageLayout/FullPageLayout) which transitively pulled the
PDF viewer, charts and lookup-studio onto /landing.
New helper src/helpers/pluginRegistry.js (lazyPlugin) defers plugin
chunks to navigation; OSS builds resolve the stub and fall back to
NotFound, so absent-plugin routes 404 harmlessly as before.
Measured /landing script requests: 201 -> 93; pdf-vendor, recharts and
Monaco no longer load until navigated to. OSS-parity build (no
src/plugins) verified.
* UN-3185: Address review — preserve security headers, tighten plugin-absent check
- nginx: drive Cache-Control from a $uri map at server scope instead of
per-location add_header. Location-level add_header replaces (not merges)
the inherited server headers, which dropped X-Content-Type-Options/
X-Frame-Options/Referrer-Policy/CSP from /assets, index.html and
runtime-config.js. Now both locations carry no add_header and inherit all
security + cache headers.
- pluginRegistry: only treat the build-time stub ('Optional plugin not
available') as plugin-absent; rethrow transient chunk-load failures of a
shipped plugin instead of masking them as NotFound.
- useMainAppRoutes: gate the OnboardProduct wrapper on PRODUCT_NAMES.unstract
(the value passed as type) rather than the map being non-empty.
* UN-3185: Extract shared lazyNamed helper (dedupe lazy-route boilerplate)
Pull the repeated 'lazy a named export' pattern into src/helpers/lazyNamed.js
and use it in Router.jsx and useMainAppRoutes.js instead of inline
.then((m) => ({ default: m.X })) / a per-file 'named' helper. Behaviour is
identical; /landing chunk count unchanged. Addresses cloud-PR review feedback
about the duplicated helper (the two route hooks import the same util).
* UN-3185: Add ErrorBoundary around lazy routes; validate lazyNamed export
- Wrap the route <Suspense> in Router.jsx with the existing ErrorBoundary and
a reload-prompt fallback. lazyPlugin/lazyNamed rethrow non-stub failures
(e.g. a transient chunk-load blip), and App rendered <Router/> with no
boundary — so such a failure would unmount the tree to a blank screen.
The boundary now contains it and offers a reload (which re-fetches the chunk).
- lazyNamed: throw a descriptive error when the requested named export is
missing instead of handing React.lazy { default: undefined } (opaque error).
Addresses greptile P1 (no ErrorBoundary) and CodeRabbit (lazyNamed validation).
* UN-3185: Scope route error handling, validate lazyPlugin export, drop dead guard
Review round 3:
- lazyPlugin: validate the resolved export (mirror lazyNamed). A shipped plugin
whose named export was renamed/removed now throws a descriptive error instead
of handing React.lazy { default: undefined }; isPluginAbsent doesn't match it,
so it re-throws to the ErrorBoundary rather than masking as NotFound.
- ErrorBoundary: support resetKeys — clear the error when a key changes (e.g.
location), so navigation recovers without a full reload.
- New LazyOutlet (content-scoped <Suspense> + nav-resettable ErrorBoundary +
<Outlet/>); PageLayout/FullPageLayout use it instead of a bare <Outlet/>.
Per-page load spinners and chunk-load failures now stay in the content area
with the shell mounted; the app-wide boundary in Router.jsx remains the
backstop and is now also location-reset. Fixes the blast-radius / no-recovery
and shell-blanks-on-first-nav issues from the single top-level boundary.
- useMainAppRoutes: remove the now-dead 'ReadOnlyReviewPage && !ReviewLayout'
warning — with lazyPlugin both are always truthy so it could never fire; the
route degrades to NotFound if manual-review is absent.
Build green (with and without src/plugins); biome clean.
* UN-3185: Use globalThis.location.reload() in RouteLoadError (SonarCloud S7764)
Prefer globalThis over window for the reload handler, matching existing
globalThis.location usage in the codebase (e.g. SideNavBar). Clears the only
open SonarCloud issue on this PR (javascript:S7764, minor code smell).
* UN-3185: Auto-reload once on chunk-load errors (stale chunk after deploy)
The route ErrorBoundary already catches a rejected dynamic import() (a
<Suspense> alone only handles the pending state). Add chunk-error handling so
the common production trigger — a stale hashed chunk after a redeploy (client
requests a filename the CDN no longer serves) — auto-recovers:
- isChunkLoadError() detects the failed-dynamic-import error across browsers.
- handleRouteError() (wired as onError on both the app-wide boundary in
Router.jsx and the content-scoped one in LazyOutlet) reloads ONCE to pick up
fresh chunk hashes, guarded by a sessionStorage timestamp so a genuinely-gone
chunk falls through to the manual Reload fallback instead of looping.
Non-chunk render errors are never auto-reloaded.
This covers the outermost lazy route elements too (e.g. FullPageLayout for
verticals, OnboardProduct for llm-whisperer), which render under the Router
boundary.1 parent 0a997e9 commit b13e5bd
9 files changed
Lines changed: 613 additions & 365 deletions
File tree
- frontend
- src
- components
- error/LazyOutlet
- widgets/error-boundary
- helpers
- layouts
- fullpage-payout
- page-layout
- routes
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
40 | 51 | | |
41 | 52 | | |
42 | 53 | | |
| |||
50 | 61 | | |
51 | 62 | | |
52 | 63 | | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
53 | 70 | | |
54 | 71 | | |
55 | 72 | | |
56 | 73 | | |
57 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
58 | 85 | | |
59 | 86 | | |
60 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
Lines changed: 24 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
7 | 13 | | |
8 | 14 | | |
9 | 15 | | |
| |||
16 | 22 | | |
17 | 23 | | |
18 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
19 | 37 | | |
20 | 38 | | |
21 | 39 | | |
| |||
37 | 55 | | |
38 | 56 | | |
39 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
40 | 64 | | |
41 | 65 | | |
42 | 66 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | 3 | | |
| 4 | + | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
8 | | - | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | 4 | | |
6 | 5 | | |
| 6 | + | |
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| |||
0 commit comments