Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141G6xqLeNRYEtviLWSB5Up
53 lines
2.5 KiB
TypeScript
53 lines
2.5 KiB
TypeScript
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=<name>.<token>), 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/<id> 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 });
|