Skip to main content

Architecture

TypeScript authoring and JavaScript runtime

Edit canonical .ts and .mts sources. TypeScript 7 emits ignored .js and .mjs siblings at the existing runtime paths. Local development loads these emitted native ES modules; production uses Rolldown to bundle the startup graph and retain lazy feature chunks. The browser does not load TypeScript directly. Use Node.js 24. npm ci installs dependencies and compiles through prepare; npm run dev-server, npm test, and the production build/check commands also compile first. With a server already running, compile after source edits before reloading. Direct Node and Playwright commands need the same compilation step.
Keep .js and .mjs specifiers in source imports, dynamic imports, worker URLs, HTML scripts, and runtime commands. For example, import './data.js' in profile.ts resolves to the authored TypeScript for checking and to emitted JavaScript at runtime. Do not edit or commit emitted siblings. Third-party vendor bundles retain their own source formats. The production and test compiler projects are separate and strict. The app, server, worker, and editor projects also retain their environment-specific safety checks; migration completion does not permit weakened flags or suppression comments. See the app’s source and compiler contract. Source modules remain the ownership boundary. Regenerate MODULE_MAP.md when modules or imports change. GitHub Actions owns the exhaustive browser and combined-coverage matrix.

File layout

Entry points

The authored entry is js/main.ts; the HTML keeps the emitted js/main.js URL. The hosted artifact rewrites it to the hashed Rolldown entry generated by scripts/build-production.mts. main.ts installs extension, foundation, cold health/Light loaders, UI shell hooks, and delegated shell actions, then calls startApp() from startup-orchestrator.ts. Startup installs global event and refresh wiring, initializes encryption/storage foundation and profile data, runs post-profile maintenance and OAuth callbacks, renders the UI, invokes the edition startup hook, and restores pending import review state. It reports failure without silently replacing unreadable profile data. Feature loaders keep expensive behavior out of the cold graph. Health modules load according to route and persisted data; chat-loader.ts loads app-ai-interaction-modules.ts on first use. Import, export, settings, sharing, and EMF have their own loader or runtime boundaries. emf-runtime.ts receives its module loader and host callbacks through app-shell-hooks.ts and loads the EMF module and stylesheet together. The composition modules remain useful grouping boundaries; they are not all eager imports of main.ts.

Trusted build-time extension boundary

app-extension-runtime.ts supports one trusted extension selected while composing an edition build. Public and self-hosted builds install the core no-op adapter through app-extension-bootstrap.ts. This is not a runtime plugin marketplace or a mechanism for loading arbitrary remote code. The contract exposes bounded hooks for Settings and onboarding slots/actions, AI provider ownership/authorization/transport/options/errors, voice request authorization and playback policy, extra sync keys/encrypted prefixes/conflict resolution/post-apply refresh, and startup. An extension must have a stable ID. If an active extension owns an AI request but does not provide a successful authorization hook, the call fails closed before transport.

Marker catalog and placement boundary

The marker catalog uses an authored TypeScript → generated TypeScript → emitted JavaScript chain. Category modules under js/marker-schema/ are the source of truth, scripts/build-marker-schema.mts generates js/marker-schema.ts, and long-lived consumers continue to use the js/schema.ts compatibility facade through its ./schema.js runtime specifier. This keeps a large catalog maintainable without forcing a broad import-graph refactor. Marker storage and display are separate boundaries. Profile values and companion metadata remain keyed by native category.markerKey; immutable gb:marker:* and custom:* IDs let marker-placement.ts apply a per-profile visual category only after getActiveData() completes calculations and unit conversion. Renderers may use displayCategoryKey, but mutations must resolve through storageDotKey. Units follow the same projection rule. unit-profiles.ts maps canonical marker storage into the profile’s International/SI (EU), Australia/New Zealand (ANZ), or US display units after calculation and range resolution. Import review and manual entry convert compatible selected units back to canonical values. Individual renderers must not carry their own regional conversion tables. The terminology registry follows a separate authoring and generation path. It is optional metadata and is intentionally outside startup, persistence, import/export, backup, sharing, and sync. See Marker model and category placement and Laboratory terminology registry. The sidebar has three conceptual groups:
  • Home: dashboard, the customizable cross-lens overview.
  • Lenses: labs, genome, body, light, insight, and recommendations. views-router.ts validates and dispatches these routes. Dashboard route/widget wiring is composed in dashboard-view-composition.ts, with the dashboard shell rendered through dashboard-page-view.ts; lab category routes render through category-page-view.ts; Labs, Genome, Body, Insight, and Recommendations pages are rendered through lens-pages.ts; shared lens page chrome and ordering live in lens-page-shell.ts; the Light page shell lives in light-page-view.ts, while channel pills and drill-down panels live in light-channel-view.ts.
  • Tools: focused utilities such as compare dates, correlations, knowledge base, custom markers, and EMF assessment entry points.
The dashboard is not a replacement for lens pages. It is a user-composed overview made from lens/tool widgets. Default widgets are ordered for a new user as: Biological Coherence, Current Focus, Current Priority, Quick Markers, Key Trends, Recommended Next Steps, Profile Context, Biometrics Overview, Biological Age, Metabolic Flexibility, and Cycle when available. Users can reorder, hide, reset, clear, and add widgets. Lens pages expose Add/Remove Dashboard toggles for widgets that can appear in the overview. Recommendations have both a dashboard widget and a dedicated recommendations route. The page aggregates data-linked recommendation candidates from Labs, Body, Light, and Genome signals, while product option rendering and affiliate disclosure remain owned by recommendations.ts.

Dependency direction

Foundation and storage code must not import feature UI. When a service needs a host action, inject a narrow callback through a configuration, runtime, or hook seam. Window exports remain compatibility entry points for existing handlers; they are not a general substitute for explicit dependencies. The current module graph has no dependency cycles. npm run architecture:check enforces zero cycle ceilings and the repository’s explicit boundary rules. The architecture contract owns these rules; the generated module map records actual imports. The module reference groups ownership areas rather than defining a second set of layer restrictions.

External dependencies

Bundled locally under vendor/: AI providers, Custom API, Local AI, xAI voice, and ElevenLabs voice are called directly from the browser. The dedicated compatibility service receives only policy-classified wearable, CAMS, NRAS, and credential-free public-page operations; it must never become a generic AI/voice proxy. The dedicated share service receives only browser-encrypted envelopes and metadata. Independent self-hosts keep their own same-origin compatibility endpoints.