Architecture
TTDash is one distributable package with two runtime halves: a Vite/React browser app and a local CommonJS server/CLI. Neutral domain contracts in shared/ connect them without allowing either half to depend on the other.
Runtime dependency model
Section titled “Runtime dependency model”src/** ───────────────┐ ├──> shared/**server/** ────────────┘ ▲ │ composed byserver/app-runtime.js ▲ │ started byserver.js
usage-normalizer.js (standalone)The enforced responsibilities are:
| Area | Ownership and allowed dependencies |
|---|---|
src/** | Browser-only React application; may use shared/**, never server/** |
server.js | Executable package/CLI shim; creates the runtime and starts the CLI path |
server/app-runtime.js | Server composition root; injects persistence, auth, HTTP, background, reporting, and auto-import services |
server/** | Local API and process runtime; may use shared/**, never src/** |
shared/** | Cross-runtime domain contracts; independent of frontend and server modules |
usage-normalizer.js | Standalone input normalization with no frontend/server dependency |
Server composition
Section titled “Server composition”The server is split into focused facades and services so server.js never becomes a catch-all:
data-runtime.jscomposes app paths, secure/atomic file I/O, mutation locks, and conservative backup merge logicauto-import-runtime.jscomposes toktrack runner discovery, commands, timeouts, progress events, version lookup, and import executionhttp-router.jsauthenticates and dispatches API/static requests through injected runtime dependencieshttp-request-guards.jsowns Host, Origin, Fetch Metadata, and JSON content-type policyremote-auth.jsowns local bootstrap auth, remote master tokens, in-memory browser sessions, and rate limitsbackground-runtime.jsowns detached instance start/stop, logs, and the instance registrystartup-runtime.jsowns startup summaries, browser opening, and current local session metadataserver-lifecycle.jsowns HTTP server lifecycle, startup sequencing, and shutdown cleanupreport/**owns Typst report data and rendering
Mutable state such as the active import lease and cached registry lookup lives inside one composed app runtime, not module-global route flags. This keeps isolated integration and browser test servers deterministic.
Persistence and shared contracts
Section titled “Persistence and shared contracts”shared/app-settings.js is the only production owner of persisted settings defaults and normalization. It consumes shared/dashboard-preferences.js, which owns supported view modes, date presets, section metadata, and default filters.
The browser uses typed adapters; the server uses the same CommonJS contracts before reading or writing settings.json. Sibling .d.ts declarations expose matching value exports and are guarded by architecture tests.
When a persisted setting changes:
- update the shared contract and defaults
- adapt server and frontend consumers
- update backup/import behavior where relevant
- extend contract and migration tests
- update the public configuration or API reference
Frontend layers
Section titled “Frontend layers”The frontend dependency direction is enforced by eslint-plugin-boundaries:
app-shell:App.tsxandmain.tsxcomponents: presentational and feature UI undersrc/components/**hooks: reusable state and dashboard orchestration undersrc/hooks/**lib-react: React-specific library moduleslib-core: framework-independent TypeScript utilitiestypes: TypeScript-only contracts
Hooks must not import components. lib-core must remain free of React, Recharts, Radix, Framer Motion, and React Query. Generic primitives belong in components/ui; feature-specific UI stays below its feature folder.
Dashboard composition
Section titled “Dashboard composition”use-dashboard-controller.ts is the public orchestration contract. Focused use-dashboard-controller-*.ts slices own effects, browser I/O, dialogs, drilldowns, derived state, shell state, and imperative actions.
Dashboard.tsx is the single production composition root that consumes the controller. It passes grouped view models to the header, filters, dialogs, command palette, and section renderer. New behavior should extend these bundles rather than reintroducing long flat prop lists.
Complex non-presentational derivations—drilldowns, heatmap data, request quality, sortable tables, and date-picker data—belong in focused src/lib modules rather than render components.
Architecture gates
Section titled “Architecture gates”Three tools cover different concerns:
| Tool | Responsibility |
|---|---|
dependency-cruiser | Whole-repository dependency boundaries and cycle detection |
eslint-plugin-boundaries | Fast frontend layer classification and import direction |
archunit plus source-graph tests | Executable placement, naming, reachability, and higher-level architecture rules |
Run the gates locally:
npm run check:depsnpm run test:architecturenpm run test:staticnpm run deps:graphDocumentation boundary
Section titled “Documentation boundary”The public site is another explicit boundary:
docs-site/src/content/docs/**is the only public content collectiondocs-site/public/**contains reviewed public assets only- the site never scans the repository root or legacy/internal
docs/**files - a publication verifier rejects untracked, ignored, symlinked, or forbidden content before deployment
When a runtime surface changes, update its canonical page and the behavior-oriented documentation contract test in the same change.
Contributor rules
Section titled “Contributor rules”- Add architecture rules only for boundaries the codebase actually intends to keep.
- Prefer the narrowest tool that proves a rule.
- Keep
server.jsan executable shim and wire new server services throughapp-runtime.js. - Centralize shared settings and dashboard defaults instead of duplicating them in adapters.
- Keep dashboard and settings-modal internals behind their public composition modules.
- Move a helper to a neutral location when it becomes cross-feature.
- Fix violations instead of adding broad allowlists.
The repository’s contributor guide covers workflow and review expectations.