Architecture
Native modules in development, production bundles in deployment
Local development loads the source as native ES modules (<script type="module">), so most UI work remains edit → reload. Hosted deployments run the Rolldown production build: it collapses the static startup graph into a hashed entry bundle while preserving feature-level lazy chunks.
That does not mean the repo has no tooling. The current repository uses npm for tests, type checks, Playwright, Vercel helpers, and a deploy-time catalog fetch. The practical development workflow is:
- Edit a file
- Reload the browser for quick local feedback
- Run the relevant checks before committing
MODULE_MAP.md records their direct imports. GitHub Actions owns the exhaustive browser and combined-coverage matrix.
File layout
Entry points
scripts/build-production.mjs; dynamically imported features remain separate lazy chunks.
main.js imports startup-loaded feature modules from app-feature-modules.js and starts startup-orchestrator.js. Foundation/privacy imports are grouped behind app-foundation-modules.js; Health & Data startup feature imports are grouped behind app-health-data-modules.js; Light & Sun startup feature imports are grouped behind app-light-sun-modules.js; import/export startup feature imports are grouped behind app-data-io-modules.js; AI/chat/settings startup feature imports are grouped behind app-ai-interaction-modules.js; UI shell startup feature imports are grouped behind app-ui-shell-modules.js. The orchestrator installs startup globals, the lazy EMF facade, app-wide event listeners, and the refresh callback, then registers the DOMContentLoaded sequence. Encryption unlock, meteo cache hydration, cross-tab broadcast, and folder backup setup are delegated to startup-foundation.js; profile migration, cache warmup, and active-profile data loading are delegated to startup-profile.js; wearable/OpenRouter callback routing lives in startup-oauth-callbacks.js; wearable runtime config/scheduler boot and non-blocking post-profile maintenance live in startup-maintenance.js; initial theme/sidebar/navigation/sync/changelog/header/chat/file-input UI bootstrap lives in startup-ui.js.
Marker catalog and placement boundary
The marker catalog uses an authoring → generated runtime → compatibility facade chain. Category modules underjs/marker-schema/ are the source of truth, scripts/build-marker-schema.mjs generates js/marker-schema.js, and long-lived consumers continue to import from js/schema.js. 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.js apply a per-profile visual category only after getActiveData() completes calculations and unit conversion. Renderers may use displayCategoryKey, but mutations must resolve through storageDotKey.
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.
Navigation and dashboard IA
The sidebar has three conceptual groups:- Home:
dashboard, the customizable cross-lens overview. - Lenses:
labs,genome,body,light,insight, andrecommendations.views-router.jsvalidates and dispatches these routes. Dashboard route/widget wiring is composed indashboard-view-composition.js, with the dashboard shell rendered throughdashboard-page-view.js; lab category routes render throughcategory-page-view.js; Labs, Genome, Body, Insight, and Recommendations pages are rendered throughlens-pages.js; shared lens page chrome and ordering live inlens-page-shell.js; the Light page shell lives inlight-page-view.js, while channel pills and drill-down panels live inlight-channel-view.js. - Tools: focused utilities such as compare dates, correlations, knowledge base, custom markers, and EMF assessment entry points.
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.js.
6-layer dependency graph
Modules in a higher layer may import from lower layers. Modules in the same layer must not import from each other — cross-layer calls within the same layer usewindow.fn() to avoid circular dependencies.
Circular dependency avoidance
The main tension is betweenviews.js (the compatibility/router facade) and modules like data.js and charts.js (which view modules depend on but which also need to trigger re-renders). Two mechanisms break cycles:
registerRefreshCallback(fn) in data.js — app-event-listeners.js registers the refresh function at startup, so data.js can trigger re-renders without importing views.js:
window.fn() calls — functions exposed via Object.assign(window, {...}) are callable from any module without creating an import edge:
External dependencies
Bundled locally undervendor/:
AI providers (OpenRouter, Routstr, PPQ, Venice, Local AI) are called directly from the browser for normal chat/import flows. Same-origin API helpers under
api/ cover hosted infrastructure such as OAuth/runtime proxying and encrypted profile share envelope storage, but they must not receive AI prompts or plaintext profile data unless explicitly documented for a new feature.