albertonline.org docs

Monorepo overview

How the albertonline.org monorepo is laid out — apps, packages, tooling, and the shared-code philosophy.

The repository is a pnpm + Turborepo monorepo. Everything the product needs — the web app, its companion apps, the mobile client, and every shared library — lives in one tree, type-checked and linted as a whole.

albertonline.org/
├── apps/         # deployable applications (Next.js, Expo, Effect servers, …)
├── packages/     # shared libraries consumed via workspace: / @repo/*
├── tools/        # vendored references (effect-smol) + standalone tooling
├── docs/         # design specs / ADRs (this Fumadocs site lives in apps/docs)
├── obsidian-vault/  # Zettelkasten engineering notes
├── turbo.json          # task graph + caching
├── pnpm-workspace.yaml # workspace globs + the version catalog
└── package.json        # root scripts (dev, build, checks, db)

Two top-level buckets

  • apps/* are the things you run or ship. The primary product is apps/web; the rest are companion surfaces and services.
  • packages/* are shared code, imported as @repo/<name>. An app should reach for a package before writing something bespoke — the whole point of the monorepo is that the API layer, auth, database, UI, and Effect plumbing are written once and reused everywhere.

Tooling

ConcernTool
Package managerpnpm 11 workspaces (nodeLinker: hoisted)
Task runner / cacheTurborepo (turbo.json)
FrameworkNext.js 16 (App Router, Turbopack always) + React 19
Typestsgo — the native TypeScript 7 preview (check-types)
Lintoxlint (pnpm lint) + the Effect JS plugin
Formatoxfmt (pnpm format:check / format:fix)
React healthreact-doctor (must stay at 0 errors)
TestsVitest (node + browser modes) + Playwright E2E
Dead code / dupesfallow (pnpm fallow) + knip
Effect systemEffect v4 (4.0.0-beta.94)

The version catalog — one source of truth

Dependency versions are centralised in the catalog: block of pnpm-workspace.yaml. A package depends on "next": "catalog:" rather than pinning a number, so an upgrade happens in one place. Doc citations of a version are gated by pnpm check:doc-versions, so READMEs can't drift from the catalog.

Always use Turbopack for next dev / next build — every app's dev/build script already passes --turbopack. A bare next dev falls back to webpack and is unsupported here.

Shared-code philosophy

  • Effect v4 everywhere it earns its keep — services are Context.Service classes with a static layer; errors are typed (Schema.TaggedError); every value crossing a boundary (HTTP body, env var, DB row, RPC payload) is decoded with a Schema, never cast.
  • Strict TypeScript, no escape hatches — no any, no as (except as const), no !, no @ts-*. A type error is a signal to fix the source of the unknown shape, not to suppress it.
  • One API boundary — apps talk to the backend through the Effect v4 RPC layer in @repo/api, not ad-hoc fetches.

Next: read the architecture guide, or jump to local development to get an app running.

On this page