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 against document.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_skipped diagnostic emission when VITE_SENTRY_DSN is unset.

READS FROM

  • import.meta.env.VITE_SENTRY_DSN — controls Sentry init.
  • import.meta.env.MODE — passed as Sentry environment and into the skip-diag payload.
  • BUILD_VERSION from src/starship-survivors/engine/core/config.ts — Sentry release tag and skip-diag payload.
  • document.getElementById('root') — non-null asserted (!); crashes hard if missing, per “crash on bad data.”

PUSHES TO

  • Sentry global (initialized as a side effect; no exports).
  • logDiag from src/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() (mutates CanvasRenderingContext2D.prototype before any draw call).

DOES NOT

  • Preload assets. Asset preloading moved into App.tsx with 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 #root throws 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, via logDiag → Supabase) when VITE_SENTRY_DSN is 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 release field equals BUILD_VERSION, so Sentry issue grouping ties to the version constant from engine/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/clientcreateRoot
    • @sentry/browserSentry.init
    • ./metagame/app/AppApp component
    • ./starship-survivors/engine/core/configBUILD_VERSION
    • ./starship-survivors/engine/telemetrylogDiag
    • ./starship-survivors/engine/diagnostics/canvas-guardsinstallCanvasGradientGuards
    • ./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() patches CanvasRenderingContext2D.prototype and uses logDiag to surface the producing callsite once. Moving the call after createRoot reopens the freeze window during the first render.
  • Sentry init is gated, not assumed. If VITE_SENTRY_DSN is missing, the build proceeds — but a sentry_init_skipped warning hits Supabase via logDiag. This is the “no Sentry events ≠ no bugs” defense from v5.113.1.
  • Non-null assertion on #root is deliberate. Crash-on-bad-data: if index.html ever 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 BrowserRouter or routing here. Routing (if any) lives inside App.

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 to src/starship-survivors/engine/boot/pre-mount.ts if reused.
  • Sentry init helperinitSentryOrLogSkip(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 patternlogDiag({ event_type: '<name>_init_skipped', ... }) is a reusable shape for any future “feature silently disabled” risk. Worth naming if a third instance shows up.