# Ashen Realms A dark-fantasy browser RPG built as an npm-workspace modular monolith: - `apps/web` — Angular 22 single-page application - `apps/api` — NestJS 11 + TypeORM + PostgreSQL API - `packages/*` — reserved shared boundaries (currently unused) This README documents the first visible vertical slice: a server-authoritative world/travel loop between two locations (Südtor von Graufurt and Verbrannte Straße) for one demo character, with no login required. ## Prerequisites - Node.js and npm (workspace-aware npm, matching the versions used by CI/your toolchain). - A reachable PostgreSQL server (PostgreSQL 13 or newer — the schema uses the built-in `gen_random_uuid()`, which needs no extensions) with a database named `ashen_realms`. Create it once, e.g.: ```powershell psql -h localhost -U postgres -c "CREATE DATABASE ashen_realms;" psql -h localhost -U postgres -c "CREATE USER ashen WITH PASSWORD 'ashen';" psql -h localhost -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE ashen_realms TO ashen;" ``` Adjust user/password/host to match your own local PostgreSQL instance; just keep `DATABASE_URL` (below) pointed at it. ## Local setup Run from the repository root: ```powershell npm install Copy-Item .env.example .env npm run db:migrate npm run db:seed npm run dev:api npm run dev:web ``` Run `dev:api` and `dev:web` in separate terminals — both watch and keep running. Once both are up: - API: `http://localhost:3000/api/...` (see routes below) - Web: `http://localhost:4200`, which proxies `/api/*` requests to `http://localhost:3000` in development (see `apps/web/proxy.conf.json`) Open `http://localhost:4200/world` to see Aric Duskwalker at the Südtor von Graufurt and travel to the Verbrannte Straße and back. ### Configuration (`.env`) `.env.example` (repo root) documents every variable. Copy it to `.env` at the repo root — `.env` is git-ignored and must never be committed. The important one for local setup is: ``` DATABASE_URL=postgresql://ashen:ashen@localhost:5432/ashen_realms ``` This is the default local PostgreSQL connection string: database name `ashen_realms`, default port `5432`. Edit it if your local PostgreSQL uses a different user, password, host, or port. All API workspace scripts (`db:migrate`, `db:seed`, `dev:api`, and `apps/api`'s own `test:e2e`) resolve this `.env` from the repository root regardless of which workspace directory npm runs the underlying command in. `synchronize` is always `false`; schema changes only happen through the checked-in migration at `apps/api/src/database/migrations/1787072400000-CreateVisibleVerticalSlice.ts`. The seed (`apps/api/src/database/seeds/vertical-slice.seed.ts`) is idempotent: running `npm run db:seed` multiple times upserts the same two locations, two connections, and one demo character without creating duplicates or resetting the character's current (live) location. ### Ports | Service | Port | Notes | |---|---|---| | API (NestJS) | `3000` | All routes are under `/api` (e.g. `/api/health`). | | Web (Angular dev server) | `4200` | Proxies `/api/*` to the API in development. | ## Available routes (first slice) ``` GET /api/health GET /api/characters/me GET /api/world/current-location GET /api/travel/current POST /api/travel { "targetLocationId": "" } ``` `POST /api/travel` accepts exactly `targetLocationId`; the global validation pipe rejects any other property (e.g. a client-supplied `arrivesAt`) with `400 Bad Request`, since `arrivesAt` is always server-derived. ## Scripts Run these from the repository root unless noted otherwise. | Script | Description | |---|---| | `npm run dev:web` | Start the Angular dev server (port 4200). | | `npm run dev:api` | Start the NestJS API in watch mode (port 3000). | | `npm run build:web` | Production build of the Angular app. | | `npm run build:api` | Production build of the NestJS app. | | `npm run build` | Both builds. | | `npm test` | Unit tests for every workspace. | | `npm run test:e2e` | API end-to-end/smoke tests (see below). | | `npm run db:migrate` | Run pending TypeORM migrations against `DATABASE_URL`. | | `npm run db:revert` | Revert the last migration. | | `npm run db:seed` | Run the idempotent demo-content seed. | ## Testing Unit tests never require a database: ```powershell npm test --workspace=@ashen-realms/api -- --runInBand ``` The API end-to-end smoke test (`apps/api/test/visible-slice.e2e-spec.ts`) always asserts `GET /api/health`. When `DATABASE_URL` is set and reachable, it additionally boots the real `AppModule` (real database, entities, and controllers) and asserts the seeded character/world responses and the `arrivesAt` validation rejection. Those database-backed assertions are skipped — not failed — when no database is configured, so `npm run test:e2e` is safe to run without any local PostgreSQL setup too: ```powershell npm run test:e2e --workspace=@ashen-realms/api -- --runInBand ``` Both builds: ```powershell npm run build:api npm run build:web ``` ## Known limitations of this first vertical slice This slice deliberately excludes (per `docs/superpowers/specs/2026-08-18-first-visible-vertical-slice-design.md`): - Authentication, user accounts, JWTs, or any login/registration flow — there is exactly one hardcoded demo character (Aric Duskwalker). - Hunting, combat, loot, inventory, equipment, quests, merchants, and currencies. - Realtime features: WebSockets, chat, guilds, CMS, object storage, or a multi-service/microservice architecture. - Ambush resolution: `ambushChance` is stored on each connection and surfaced only as a coarse `LOW`/`HIGH` danger rating; it is never rolled. - Production containerization or NestJS static hosting. - Client-side clock skew can shift how the travel countdown *displays*, but arrival is always confirmed by the server (`GET /api/travel/current` / `GET /api/world/current-location`), never inferred locally. Only two locations and one directed pair of connections exist (`south-gate` ⇄ `burned-road`), each with a fixed 10-second travel duration.