# Frontend architecture The HTTP frontend is a strict TypeScript application built with esbuild. Rust embeds deterministic JavaScript output from `assets/web/generated`; Cargo does not run Node.js or contact the network. ## Runtime graph `frontend/src/bootstrap.ts` is the only first-party script referenced by the HTML document. Its imports establish startup order for WebGL support, shared UI services, decoder dispatch, the local Leaflet AIS adapter, and the application coordinator. Leaflet and the Opus decoder remain isolated vendored scripts. Feature entries under `frontend/src/plugins` are ESM bundles. The typed plugin loader imports them by feature group and keeps expensive map, scheduling, and decoder behavior lazy. Shared code is emitted as content-hashed chunks. The decode-history worker is an independent entry compiled against Web Worker globals. Dependencies point from application and feature code toward `core` and `api`. `api/generated.ts` contains Rust wire formats; `api/client.ts` and focused parsers validate untrusted HTTP, SSE, WebSocket, and worker data before it is used as typed application state. ## Browser host contract Separate lazy bundles cannot share module instances with the stable application entry, so they use three intentional host namespaces: | Global | Purpose | Mutation policy | | --- | --- | --- | | `window.trx` | Application state, core services, and feature registrations | The root is frozen; lazy features may register only their documented `modules.*` service. | | `window.trxPluginRuntime` | Typed decoder registration and message dispatch | Runtime object is installed once; plugins register lifecycle handlers through its API. | | `window.trxUi` | Notifications, confirmations, tab accessibility, and control presentation | Installed once by `ui-core.ts`; consumers call methods but do not replace them. | The WebGL adapter exposes `createTrxWebGlRenderer`, `trxParseCssColor`, `trxHslToRgba`, and `trxClearCssColorCache` for the application bundle. Leaflet adds `L.TrxAisTrackSymbol` and `L.trxAisTrackSymbol` to the vendored Leaflet namespace. The following transitional properties are explicitly part of the lazy-feature host contract and are declared in `app.ts`: `lastSpectrumData`, `lastFreqHz`, `currentBandwidthHz`, `ft8BaseHz`, `getDecodeHistoryRetentionMs`, `applyDecodeHistoryRetention`, `getDecodeRigMeta`, `renderRdsOverlays`, `buildAisVesselUrl`, `trxScheduleUiFrameJob`, `takeSchedulerControlForDecoderDisable`, `navigateToTab`, `_syncRecorderState`, and `refreshRdsUi`. Optional callbacks owned by lazy features are `refreshCwTonePicker`, `updateFt8RfDisplay`, `clearSatPredictionDom`, `syncWefaxToggle`, `updateAisBar`, `updateVdesBar`, `updateAprsBar`, `updateFt8Bar`, `updateSatLiveState`, `applyCwAutoUi`, and `applyCwAutoUiFromServer`. This list is closed: new standalone mutable `window` properties are not an accepted integration mechanism. Extend an existing typed service or introduce an imported interface instead. Removing transitional properties as feature boundaries become directly importable remains preferable. ## Build and verification The generated directory is removed before every build, preventing orphaned compatibility bundles. Stable feature entry names are allowlisted by the Rust asset manifest; shared chunks use content hashes. The generic Rust handler rejects unknown names and unsupported MIME types and serves embedded assets with compression, ETags, and immutable caching. CI installs from `package-lock.json`, caches only npm downloads, type-checks the window and worker environments separately, lints, runs unit and DOM tests, starts the application in Chromium, regenerates Rust contracts and bundles, checks for drift, and runs REUSE validation after generation. See `frontend/src/README.md` for local commands and `docs/ts-migration-plan.md` for the migration decisions and completion gates.