src/main.tsx — React Entry Point
PURPOSE
Browser entry point. Wires three pre-mount concerns (canvas-gradient guards, Sentry init, React root) and renders the <App /> shell into #root. Ships zero gameplay logic; this is the boot-order contract for everything downstream.
OWNS
- The single
createRoot(...)call againstdocument.getElementById('root'). - The boot-order contract: canvas guards FIRST, Sentry init SECOND, React mount THIRD. Reordering these reintroduces shipped regressions (see Pattern notes).
- The Sentry client config (
dsn,release,tracesSampleRate: 0.1,environment). - The
sentry_init_skippeddiagnostic emission whenVITE_SENTRY_DSNis unset.
READS FROM
import.meta.env.VITE_SENTRY_DSN— controls Sentry init.import.meta.env.MODE— passed as Sentryenvironmentand into the skip-diag payload.BUILD_VERSIONfromsrc/starship-survivors/engine/core/config.ts— Sentryreleasetag and skip-diag payload.document.getElementById('root')— non-null asserted (!); crashes hard if missing, per “crash on bad data.”
PUSHES TO
Sentryglobal (initialized as a side effect; no exports).logDiagfromsrc/starship-survivors/engine/telemetry→ Supabase when DSN is absent (event_type: 'sentry_init_skipped',level: 'warning').- React DOM at
#root. - Canvas 2D prototype patches via
installCanvasGradientGuards()(mutatesCanvasRenderingContext2D.prototypebefore any draw call).
DOES NOT
- Preload assets. Asset preloading moved into
App.tsxwith a progress bar — comment at line 15 calls this out explicitly. No fire-and-forget sprite loads here. - Configure Zustand stores, the engine loop, or routing.
- Register service workers or PWA hooks.
- Swallow init errors — missing
#rootthrows via the non-null assertion; missing DSN is logged-and-continue (not an error). - Read from query string, localStorage, or URL params.
Signals
event_type: 'sentry_init_skipped'(warning, vialogDiag→ Supabase) whenVITE_SENTRY_DSNis empty/undefined. Payload carries{ mode, build }. This exists because a previous build (v5.113.1) had Sentry silently disabled and a zoom bug went invisible — the diag closes that gap.- Sentry
releasefield equalsBUILD_VERSION, so Sentry issue grouping ties to the version constant fromengine/core/config.ts. tracesSampleRate: 0.1— 10% of transactions sampled for performance traces.
Entry points
- Imported by:
index.html(Vite injects this as the module entry). - Imports (top-level side effects only — no exports):
react-dom/client→createRoot@sentry/browser→Sentry.init./metagame/app/App→Appcomponent./starship-survivors/engine/core/config→BUILD_VERSION./starship-survivors/engine/telemetry→logDiag./starship-survivors/engine/diagnostics/canvas-guards→installCanvasGradientGuards./index.css(global styles, side-effect import)
- Exports: none. This file is run for its side effects.
Pattern notes
- Canvas guards run before React mounts on purpose. Sentry STARSHIPSURVIVORS-Y (2026-05-03) traced a hard freeze in v5.163.2 to a per-frame “non-finite radial gradient” loop.
installCanvasGradientGuards()patchesCanvasRenderingContext2D.prototypeand useslogDiagto surface the producing callsite once. Moving the call aftercreateRootreopens the freeze window during the first render. - Sentry init is gated, not assumed. If
VITE_SENTRY_DSNis missing, the build proceeds — but asentry_init_skippedwarning hits Supabase vialogDiag. This is the “no Sentry events ≠ no bugs” defense from v5.113.1. - Non-null assertion on
#rootis deliberate. Crash-on-bad-data: ifindex.htmlever ships without<div id="root">, mount fails loudly instead of rendering into the void. - No
<StrictMode>wrapper. React 19 + Canvas 2D + Zustand stores; double-mount semantics would double-init the engine loop. Intentional omission. - No
BrowserRouteror routing here. Routing (if any) lives insideApp.
EXTRACT-CANDIDATE
The two-step “guards + Sentry-or-diag” preamble is currently inline. Shared concepts worth tracking if a second entry point (e.g., a debug harness or admin shell) ever lands:
- Pre-render init contract — the ordered triple
(canvas-guards → sentry-init-or-skip-diag → mount). Could move tosrc/starship-survivors/engine/boot/pre-mount.tsif reused. - Sentry init helper —
initSentryOrLogSkip(dsn, release, mode)would dedupe the DSN-gate + skip-diag pattern; today it lives only here, so extraction is premature (rule of three). - Telemetry-on-missing-config pattern —
logDiag({ event_type: '<name>_init_skipped', ... })is a reusable shape for any future “feature silently disabled” risk. Worth naming if a third instance shows up.