Files
pentagi/frontend/index.html
T
Sergey Kozyrenko 99c8b6786c fix: recover from stale-chunk and DOM-desync SPA crashes
Two crashes seen in production, both reproduced on the live app:

- "Failed to fetch dynamically imported module": after a redeploy rotates the
  hashed chunk filenames, an open tab imports a deleted one. The server answered a
  missing /assets/* with 301 -> index.html (HTML for a JS module -> a MIME
  failure); it now returns 404 + no-store. The client listens for Vite's
  vite:preloadError and reloads once (debounced) to pull the current build. Hashed
  assets are served immutable; index.html and SPA routes no-cache.

- "Failed to execute 'removeChild' ... not a child of this node": an external
  agent (a browser extension or auto-translation) mutates the DOM React owns,
  desyncing reconciliation. A root react-router errorElement catches this
  commit-phase crash and self-heals with a debounced reload, instead of React
  Router's dead default error screen. translate="no" opts the English-only UI out
  of the one trigger it can prevent (browser translation); the errorElement covers
  the rest regardless of source.

Verified by reproducing both on the live old build (missing-chunk 301->HTML;
extension/translation DOM mutation -> the exact removeChild crash) and confirming
the fixed build recovers from each. Adds chunk-reload + RouteErrorBoundary unit
tests and a static-serving integration test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 17:32:34 +07:00

85 lines
2.2 KiB
HTML

<!doctype html>
<html
lang="en"
translate="no"
>
<head>
<meta charset="UTF-8" />
<!-- The UI is English-only. Browser auto-translation is one known trigger of
a DOM desync (it swaps text nodes React owns), so opt the document out of
it. Defense-in-depth only: the route errorElement recovers from such
desyncs (extension or translation) regardless of source. -->
<meta
name="google"
content="notranslate"
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<title>PentAGI</title>
<link
rel="icon"
type="image/png"
href="/favicon/favicon-96x96.png"
sizes="96x96"
/>
<link
rel="icon"
type="image/svg+xml"
href="/favicon/favicon.svg"
/>
<link
rel="shortcut icon"
href="/favicon/favicon.ico"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/favicon/apple-touch-icon.png"
/>
<link
rel="manifest"
href="/favicon/site.webmanifest"
/>
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-regular.woff2"
crossorigin="anonymous"
/>
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-500.woff2"
crossorigin="anonymous"
/>
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-600.woff2"
crossorigin="anonymous"
/>
<link
rel="preload"
as="font"
type="font/woff2"
href="/fonts/inter-700.woff2"
crossorigin="anonymous"
/>
</head>
<body class="bg-background font-sans antialiased">
<div id="root"></div>
<script
type="module"
src="/src/main.tsx"
></script>
</body>
</html>