import { mount } from "svelte"; import Announcement from "./Announcement.svelte"; import * as Sentry from "@sentry/browser"; /** A seat link carries its token in the fragment (#seat=.), and * the history breadcrumb for taking it out of the address bar still names * it, so it is cut from every report before one leaves the browser. */ function scrub(text: string): string { return text.replace(/(#seat=)[^&#\s"']*/g, "$1…"); } // Errors only — no tracing, no replay. Without a DSN baked at build time // (VITE_SENTRY_DSN in .env.production) this is entirely inert. if (import.meta.env.VITE_SENTRY_DSN) { Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN, environment: "production", // Facebook's in-app browser injects its own scripts (iabjs://) and // reports their failures as ours when the page unloads. // Extensions run their own scripts in the page, and their failures are not ours. denyUrls: [/^iabjs:\/\//, /^(chrome|moz|safari(-web)?)-extension:\/\//], ignoreErrors: ["Java object is gone"], beforeSend: (event) => JSON.parse(scrub(JSON.stringify(event))), beforeBreadcrumb: (crumb) => JSON.parse(scrub(JSON.stringify(crumb))), }); } // A page loaded before a deploy may ask for a chunk the deploy retired. // Vite reports the failed import here; one reload fetches the live bundle. // The flag keeps a broken build from reloading forever. window.addEventListener("vite:preloadError", (e) => { e.preventDefault(); try { if (sessionStorage.getItem("wizwar-reloaded")) return; sessionStorage.setItem("wizwar-reloaded", "1"); } catch { /* storage may be unavailable; reload anyway */ } location.reload(); }); try { sessionStorage.removeItem("wizwar-reloaded"); } catch { /* ignore */ } // /watch/ is the public share page — one turn, anyone may look. It // loads its own component so the full app (and its live socket appetite) // stays out of a shared link's way. const target = document.getElementById("app")!; const page = location.pathname.startsWith("/watch/") ? import("./SharePage.svelte") : import("./App.svelte"); // A retired chunk resolves to nothing once the handler above has claimed it; // the reload is already on its way, so there is nothing to mount. page.then((m) => m && mount(m.default, { target })); // The hall-wide notice sits above the game, apart from it: every game of Kestrel's Hall shows the same one. mount(Announcement, { target: document.body, anchor: document.body.firstChild ?? undefined });