Development Setup
Environment setup and day-to-day development workflows for TimeTiles.
Quick Start
git clone https://github.com/jfilter/timetiles.git
cd timetiles
make init # installs deps, starts DB, runs migrations, seeds, starts dev server- App: localhost:3000
- Dashboard: localhost:3000/dashboard
- Docs: localhost:3001 (run
pnpm devinapps/docs)
See Quick Start for prerequisites and database mode options.
Commands
| Command | What it does |
|---|---|
make dev | Start dev server (auto-starts database) |
make check | Lint + typecheck all packages |
make test | Run unit + integration tests |
make test-e2e | Run Playwright E2E tests |
pnpm exec oxfmt <file> | Format the reported files only |
make migrate | Run database migrations |
make seed | Seed database with sample data |
make fresh | Full reset: database + migrate + seed |
make status | Check environment health |
make format and pnpm format run only the formatter, without lint autofixes. Prefer named files to avoid unrelated formatting changes. Avoid broad eslint --fix runs: they can strip suppressions for oxlint-owned rules. make check-ai reports whole-tree formatting failures even when other checks are scoped.
make clean deletes the development database; make fresh does the same and then migrates and reseeds it. Back up any data you need before using either command. Docker cleanup is limited to this project’s Compose resources; it does not prune other projects’ resources.
make check-ai without arguments runs fast Oxlint and TypeScript checks across the packages, including Shared and the scraper SDK. It does not run ESLint; use pnpm lint for the full workspace lint gate. PACKAGE=web also runs ESLint, while FILES="..." uses Oxlint and filters TypeScript diagnostics to the requested files. Package selectors are web, docs, ui, and timescrape; the legacy scraper alias selects the TimeScrape runner, not the SDK.
ESLint runs from the repository root so SonarJS can resolve dependency versions from pnpm-workspace.yaml. The shared pnpm -w lint:eslint <root-relative paths> command uses ESLint’s file-based configuration lookup, preserving each package’s rules and ignores. Package lint scripts keep separate cache files; use those scripts or pnpm lint for normal checks. The Oxlint bridge resolves its configuration relative to the repository, not the current working directory.
Database Seeding
make seed ARGS="development" # Rich, realistic data (default)
make seed ARGS="testing --truncate" # Fast, deterministic data for tests
make seed ARGS="demo" # Polished data for demosAvailable presets: minimal, testing, e2e, development, demo, benchmark. Add --truncate to clear existing data first.
Workflow
- Create a feature branch from
main - Make your changes
- Run
make check— lint and typecheck must pass - Write tests for new functionality
- Commit using conventional commits
- Open a pull request
Code Standards
- TypeScript strict mode — all code must pass strict type checking
- Named imports only —
import { foo } from 'bar' - No
console.log— uselogger.info()/logError()from@/lib/logger - React Query for all data fetching — never fetch directly in components
- Coordinates in
[longitude, latitude]order (GeoJSON standard) - PostGIS for spatial queries — no client-side geo computations
Layered Architecture
The lib/ directory follows a strict import hierarchy:
Layer 0 — Foundation
utils/, security/, types/, constants/, geospatial/, filters/, definitions/, schemas/
Layer 1 — Infrastructure (can import Layer 0)
services/, database/, middleware/
Layer 2 — Domain (can import Layer 0 + 1)
ingest/, account/, export/, email/, collections/, blocks/
Layer 3 — Application
api/, hooks/, jobs/, config/, context/, globals/, openapi/, seed/, data-packages/, metadata/Each layer can only import from the same layer or below. packages/eslint-config/base.js enforces this and declares per-file exceptions, including foundation configuration helpers and the Payload composition root.
Migrations
make migrate-create # Auto-generate from schema changes
make migrate # Apply pending migrationsNever create migration files manually — always use Payload’s generator.
Troubleshooting
| Problem | Solution |
|---|---|
| Port 3000 in use | make kill-dev |
| Database connection error | make status, then make ensure-infra |
| Stale state | make fresh |
| TypeScript errors after updates | pnpm install && make check |
Further Reading
- Commit Guidelines — Conventional commit format and scope rules
- Testing Guidelines — Test types, mocking rules, and patterns
- Documentation Guide — JSDoc and MDX conventions
- Payload Deadlocks — Prevention patterns for CMS hooks