Repo Architecture
Compass is a TypeScript monorepo with four packages and one shared event domain.
Package Map
packages/web
The React frontend. It owns:
- app startup and routing
- auth/session-aware UI
- event and task interactions
- local offline storage
- SSE listeners (
EventSource)
Key entrypoints:
packages/web/src/index.tsxpackages/web/src/components/App/App.tsxpackages/web/src/routers/index.tsxpackages/web/src/views/Root.tsx
packages/backend
The Express + MongoDB backend. It owns:
- route registration
- Supertokens session enforcement
- event CRUD and recurrence processing
- Google Calendar sync
- SSE fanout
Key entrypoints:
packages/backend/src/app.tspackages/backend/src/servers/express/express.server.tspackages/backend/src/servers/sse/sse.server.ts
packages/core
The shared domain layer. It owns:
- Zod schemas and TypeScript types
- shared constants
- date/event utilities
- mapping logic between Compass and provider formats
High-value files:
packages/core/src/types/event.types.tspackages/core/src/types/type.utils.tspackages/core/src/constants/core.constants.tspackages/core/src/constants/sse.constants.ts
packages/scripts
The CLI and database maintenance package. It owns:
- build commands
- delete flows
- database migrations and seeders
Entry point:
packages/scripts/src/cli.ts
Runtime Boundaries
Web -> Core
The web package imports shared event/task/date concepts from core and should not redefine them locally unless the data is UI-specific.
Backend -> Core
The backend uses core for shared validation, event categories, recurrence scopes, constants, and SSE event names.
Web <-> Backend
The web talks to the backend through:
- HTTP APIs
- SSE events
- shared domain types from
core
Startup Paths
Frontend boot
packages/web/src/index.tsx does this in order:
- initialize storage
- start saga middleware
- initialize session tracking
- render
<App />
<App /> then installs provider trees and the router.
Backend boot
packages/backend/src/app.ts does this in order:
- create Express app
- create HTTP server
- register HTTP routes (SSE is opened per authenticated
GET /api/events/stream) - start Mongo
- listen on the configured port
Main Architectural Patterns
Backend route pattern
routes.config.ts -> controller -> service -> query/mongo
This is the standard pattern for new HTTP behavior.
Web state pattern
Web state is not single-system:
- Redux Toolkit slices hold app state and async state
- redux-saga handles side effects
- Elf stores are used for event entity management and draft/active state
- IndexedDB stores offline events/tasks
Treat this as an intentional mixed architecture, not an inconsistency to "fix" casually.
Shared schema pattern
The repo prefers:
- define schema with Zod
- export inferred TypeScript type
- consume the same contract in web and backend
Where Cross-Cutting Changes Usually Land
- New event field:
coreschema, backend parsing/persistence, web editors/selectors/tests - New backend endpoint: backend route/controller/service plus maybe shared type in
core - New SSE event:
coreconstants/types, backendsse.server/publish, web SSE hook consumer - New local persistence behavior: web storage adapter, migration runner, tests