Repo Architecture
Compass is a TypeScript monorepo with five packages and one shared event domain.
Package Map
packages/web
The React frontend. It owns:
- app startup and routing
- auth/session-aware UI
- event 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/sync
The provider synchronization service. It owns:
- provider connection and credential custody
- Google Calendar adapters and notification verification
- sync jobs, scheduling, reconciliation, and subscription renewal
- provider calendars, events, invalidations, and sync-resource persistence
- sync readiness, health telemetry, and graceful shutdown
Key entrypoints:
packages/sync/src/app.tspackages/sync/src/server/sync.server.tspackages/sync/src/domain/sync-job-worker.service.tspackages/sync/src/config/sync.config.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.contracts.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
- operational maintenance commands
Entry point:
packages/scripts/src/cli.ts
Runtime Boundaries
Web -> Core
The web package imports shared event/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.
Sync -> Core
The sync service uses core for shared logging and domain contracts while
keeping provider credentials, job orchestration, and provider-specific adapters
inside packages/sync.
Web <-> Backend
The web talks to the backend through:
- HTTP APIs
- SSE events
- shared domain types from
core
Backend <-> Sync
The backend remains the browser-facing API and SSE boundary. The sync service
exposes authenticated internal HTTP routes and change feeds for provider
connection, command, notification, and availability work. Keep shared wire
contracts explicit and provider implementation details inside packages/sync.
Startup Paths
Frontend boot
packages/web/src/index.tsx does this in order:
- initialize storage
- 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
Sync boot
packages/sync/src/app.ts does this in order:
- load and validate sync configuration
- create the HTTP app, lifecycle registries, and storage dependencies
- bind the HTTP port so liveness is available
- connect MongoDB and install readiness checks
- start retention and health sweeps
- in active provider-configured mode, start job, reconciliation, and subscription schedulers
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:
- Zustand stores hold transient client state (draft, view dates/sidebar, cmd palette, user metadata)
- TanStack Query owns persisted event reads/mutations and deduplicates keyed event reads
- IndexedDB stores offline events
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 provider sync behavior: sync provider port/adapter, job orchestration and storage, backend integration boundary, and affected web state
- New local persistence behavior: web storage adapter, migration runner, tests