Skip to content

Monorepo Layout

tripplan.ing uses npm workspaces to organize code across apps and shared packages. This page explains the structure, boundaries, and import rules.

Workspace structure

tripplan-ing/
├── apps/
│   ├── event-site/           # Main SvelteKit application
│   │   ├── src/
│   │   │   ├── hooks.server.ts
│   │   │   ├── lib/
│   │   │   │   ├── server/   # Server-only code (auth, DB, runtime, platform)
│   │   │   │   ├── components/
│   │   │   │   └── types.ts
│   │   │   └── routes/       # SvelteKit routes (admin, platform, api, public)
│   │   ├── drizzle/          # Generated migration files
│   │   ├── wrangler.toml     # Cloudflare Worker config
│   │   └── package.json
│   └── docs/                 # VitePress documentation site
│       ├── .vitepress/config.ts
│       └── **/*.md
├── packages/
│   ├── platform-shared/      # Platform contracts, validation, lifecycle rules
│   │   ├── src/
│   │   │   ├── event-lifecycle.ts
│   │   │   └── validation.ts
│   │   └── package.json
│   ├── runtime-core/         # Shared runtime primitives (KV interface, env helpers)
│   │   ├── src/
│   │   │   └── kv.ts
│   │   └── package.json
│   └── ui/                   # Shared UI components (if any)
│       └── package.json
├── scripts/                  # Repository-level scripts
├── .github/workflows/        # CI/CD pipelines
├── Makefile                  # Convenience commands
└── package.json              # Root workspace config

Package roles

apps/event-site

The main application. Contains all business logic, routes, database schema, and runtime implementations. This is where most development happens.

packages/platform-shared

Cross-cutting platform contracts that are shared between the app and any future consumers:

  • Event lifecycle: State machine rules (isLifecycleReadOnly(), valid transitions)
  • Validation: Email normalization, input sanitization

Import as:

typescript
import { isLifecycleReadOnly } from '@tripplan/platform-shared/event-lifecycle';
import { normalizeEmail } from '@tripplan/platform-shared/validation';

packages/runtime-core

Shared runtime primitives — interfaces that both runtimes (Cloudflare and Node) implement:

  • KV interface: KvStore type with get, put, delete methods

Import as:

typescript
import type { KvStore } from '@tripplan/runtime-core/kv';

packages/ui

Shared UI components (Svelte). Currently minimal.

Boundary rules

What goes where

Code typeLocation
Event business logicapps/event-site/src/lib/server/
Platform management logicapps/event-site/src/lib/server/platform/
Database schemaapps/event-site/src/lib/server/db/schema.ts
Runtime abstractionapps/event-site/src/lib/server/runtime/
Shared interfaces (KV, etc.)packages/runtime-core/
Lifecycle rules, validationpackages/platform-shared/
UI componentsapps/event-site/src/lib/components/ or packages/ui/

Import rules

  1. Apps can import packages: apps/event-site can import from @tripplan/platform-shared and @tripplan/runtime-core
  2. Packages cannot import apps: Shared packages must not depend on app code
  3. Packages can import other packages: platform-shared can import from runtime-core if needed
  4. Use package names for cross-workspace imports: Always @tripplan/platform-shared/..., never relative paths across workspace boundaries

Enforcing boundaries

Run the boundary checker to validate import rules:

bash
npm run check:boundaries

This checks that:

  • Shared packages don't import from apps/
  • Cross-workspace imports use package names
  • No circular dependencies between packages

Adding shared code

Before adding code to a shared package, verify it meets the criteria:

  • platform-shared: Must be a platform contract, lifecycle rule, or validation utility. No runtime-specific code.
  • runtime-core: Must be an interface or primitive used by both runtimes. No implementation code.

If the code is only used by apps/event-site, keep it in apps/event-site/src/lib/. Don't prematurely extract to packages.

Build and CI

The CI workflow (.github/workflows/apps-ci.yml) handles:

  1. Install: npm ci at root (installs all workspaces)
  2. Check: npm run check (TypeScript + Svelte + boundary checks)
  3. Test: npm test
  4. Build: npm run build (builds the Worker)
  5. Deploy: Wrangler deploy with migration and secret sync

Docs have a separate workflow (.github/workflows/docs-deploy.yml).

Released under the MIT License.