diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 0000000..3b2b44d --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -0,0 +1,319 @@ +# Architecture + +**Analysis Date:** 2026-04-27 + +## Pattern Overview + +**Overall:** NestJS modular backend + React feature-based frontend with real-time WebSocket synchronization + +**Key Characteristics:** +- Modular NestJS architecture with feature-based modules (Auth, Characters, Campaigns, Equipment, Battle) +- Feature-first React organization with shared components and hooks +- WebSocket Gateway real-time sync for character and battle state +- Role-based access control (ADMIN, GM, PLAYER) enforced globally +- Prisma ORM for PostgreSQL data persistence +- JWT-based stateless authentication + +## Layers + +**Backend: Controller Layer** +- Purpose: HTTP request handling and routing +- Location: `server/src/modules/*/[feature].controller.ts` +- Contains: REST endpoints with decorators (@Post, @Get, @Put, @Patch, @Delete) +- Depends on: Services, DTOs, Guards, Decorators +- Used by: HTTP clients (React frontend) +- Examples: + - `server/src/modules/characters/characters.controller.ts` - Character CRUD + - `server/src/modules/campaigns/campaigns.controller.ts` - Campaign management + - `server/src/modules/equipment/equipment.controller.ts` - Equipment search/browse + - `server/src/modules/auth/auth.controller.ts` - Login/Register + +**Backend: Service Layer** +- Purpose: Business logic, data processing, validation +- Location: `server/src/modules/*/[feature].service.ts` +- Contains: Methods for creating, updating, deleting entities; complex calculations +- Depends on: PrismaService, other Services, external APIs (Claude) +- Used by: Controllers, Gateways, other Services +- Examples: + - `server/src/modules/characters/characters.service.ts` - Character operations, access checks + - `server/src/modules/characters/alchemy.service.ts` - Alchemy system logic (formulas, prepared items, vials) + - `server/src/modules/characters/pathbuilder-import.service.ts` - Pathbuilder JSON parsing + - `server/src/modules/equipment/equipment.service.ts` - Equipment search with filters + - `server/src/modules/battle/battle.service.ts` - Battle session and token management + +**Backend: Gateway Layer (WebSocket)** +- Purpose: Real-time bidirectional communication for live updates +- Location: `server/src/modules/*/[feature].gateway.ts` +- Contains: Socket.io event handlers, authentication, room management +- Depends on: JwtService, PrismaService, Services +- Used by: React WebSocket hooks +- Examples: + - `server/src/modules/characters/characters.gateway.ts` - Character HP, conditions, items, alchemy real-time sync + - `server/src/modules/battle/battle.gateway.ts` - Battle token movement, HP, initiative real-time sync + +**Backend: Persistence Layer** +- Purpose: Database abstraction and ORM +- Location: `server/src/prisma/prisma.service.ts` +- Contains: Prisma client wrapper, query interface +- Depends on: PostgreSQL database +- Used by: All Services +- Data Models defined in: `server/prisma/schema.prisma` + +**Frontend: Feature Modules** +- Purpose: Isolated feature domains with components, hooks, types +- Location: `client/src/features/[feature]/` +- Contains: Components, hooks, index.ts barrel exports +- Examples: + - `client/src/features/auth/` - Login/Register pages, useAuthStore hook + - `client/src/features/characters/` - Character sheet page, modals, utilities + - `client/src/features/campaigns/` - Campaign list/detail pages + - `client/src/features/battle/` - Battle canvas, tokens + - `client/src/features/library/` - Battle maps, NPC templates (combatants) + +**Frontend: Shared Layer** +- Purpose: Reusable components, hooks, utilities, types across features +- Location: `client/src/shared/` +- Contains: + - Components: `ui/` (shadcn/ui), `layout.tsx`, `protected-route.tsx` + - Hooks: `use-character-socket.ts`, `use-battle-socket.ts` + - Lib: `api.ts` (API client), `utils.ts` (helpers) + - Types: `index.ts` (all TypeScript interfaces) +- Used by: All features + +**Frontend: Router** +- Purpose: Navigation and route protection +- Location: `client/src/App.tsx` +- Contains: React Router v6 routes, protected route wrapper +- Depends on: React Router, Feature components +- Entry point: `client/src/main.tsx` + +**Frontend: State Management** +- Purpose: Client-side state persistence +- Location: `client/src/features/auth/hooks/use-auth-store.ts` +- Contains: Zustand store for authentication state +- Used by: Auth-related components, ProtectedRoute + +## Data Flow + +### Character Update (Real-Time WebSocket Sync) + +1. **User Action** (React Component) + - User damages character in `client/src/features/characters/components/hp-control.tsx` + - Calls `api.updateCharacterHp()` → PATCH `/campaigns/:id/characters/:id/hp` + +2. **Controller** (`server/src/modules/characters/characters.controller.ts`) + - Receives HTTP request + - Validates JWT token via `JwtAuthGuard` + - Calls `CharactersService.updateHp()` + +3. **Service** (`server/src/modules/characters/characters.service.ts`) + - Checks campaign access (GM or campaign member) + - Checks character ownership (owner or GM can edit) + - Updates character HP in Prisma + - Emits WebSocket event via `CharactersGateway.broadcast()` + +4. **Gateway Broadcast** (`server/src/modules/characters/characters.gateway.ts`) + - Broadcasts `character_update` event to all clients in character room + - Update type: `'hp'` + - Payload: `{ hpCurrent, hpTemp, hpMax }` + +5. **Client Socket Hook** (`client/src/shared/hooks/use-character-socket.ts`) + - Listens for `character_update` event + - Routes to appropriate callback: `onHpUpdate?.(data)` + - Component state updates via React state + +6. **Component Re-render** + - Character sheet displays new HP values + - All other connected clients see update in real-time + +### Equipment Database Search + +1. **User Search** (React Component) + - User searches equipment in `client/src/features/characters/components/add-item-modal.tsx` + - Calls `api.searchEquipment({ query, category, filters, page, limit })` + +2. **Controller** (`server/src/modules/equipment/equipment.controller.ts`) + - Receives GET `/equipment` with query params + - Calls `EquipmentService.search()` + +3. **Service** (`server/src/modules/equipment/equipment.service.ts`) + - Builds Prisma where clause from filters + - Queries Equipment table (5,482 items) + - Returns paginated results with pagination metadata + - Includes category list for UI dropdown + +4. **Component Display** + - Results displayed with pagination controls + - User can view equipment details, add to inventory + - New item created via `api.addCharacterItem()` + +### Character Creation (Pathbuilder Import) + +1. **User Upload** (React Component) + - User uploads Pathbuilder JSON in `client/src/features/characters/components/import-character-modal.tsx` + - Calls `api.importCharacterFromPathbuilder(pathbuilderJson)` + +2. **Controller** (`server/src/modules/characters/characters.controller.ts`) + - POST `/campaigns/:id/characters/import` + - Calls `PathbuilderImportService.importCharacter()` + +3. **Pathbuilder Import Service** (`server/src/modules/characters/pathbuilder-import.service.ts`) + - Parses Pathbuilder JSON structure + - Extracts abilities, skills, feats, spells + - Creates Character with all related entities (CharacterAbility, CharacterSkill, etc.) + - Stores raw pathbuilderData for future reference + +4. **Database Persistence** + - Character created with all nested relations + - Each skill, feat, spell stored as separate records + - Character ready for real-time sync + +### Battle Session Synchronization + +1. **Token Movement** (React Component) + - User drags token on battle canvas + - Calls `api.moveBattleToken(positionX, positionY)` + +2. **Battle Gateway** (`server/src/modules/battle/battle.gateway.ts`) + - Receives movement update + - Broadcasts `battle_update` event with type `'token_moved'` + - All clients in session room receive update + +3. **Client Display** + - All participants see token at new position + - No UI lag — updates are instant + +### Alchemy System State + +1. **Prepare Items** (React Component) + - User selects formulas to prepare daily in `client/src/features/characters/components/alchemy-tab.tsx` + - Calls `api.dailyPreparation(items)` + +2. **Alchemy Service** (`server/src/modules/characters/alchemy.service.ts`) + - Validates character has formulas + - Creates CharacterPreparedItem records + - Updates CharacterAlchemyState (versatileVialsCurrent, advancedAlchemyMax) + - Emits WebSocket `alchemy_prepared` and `alchemy_state` updates + +3. **Real-Time Sync** + - UI updates showing prepared items and vial tracker + - Ready for combat use + +## Key Abstractions + +**Prisma ORM Service:** +- Purpose: Database abstraction, query builder +- Location: `server/src/prisma/prisma.service.ts` +- Pattern: Singleton service injected into all modules +- Used for: All CRUD operations, complex queries with relations + +**JWT Strategy & Guards:** +- Purpose: Authentication and authorization +- Location: + - `server/src/modules/auth/strategies/jwt.strategy.ts` - JWT validation + - `server/src/modules/auth/guards/jwt-auth.guard.ts` - Global auth enforcement + - `server/src/modules/auth/guards/roles.guard.ts` - Role-based access control +- Pattern: NestJS guards executed globally on every request +- Metadata: `@Roles()` and `@Public()` decorators control per-endpoint behavior + +**API Client Service:** +- Purpose: HTTP communication abstraction +- Location: `client/src/shared/lib/api.ts` +- Pattern: Singleton class with axios instance +- Features: Token management, auto-retry, auth interceptors, 401 handling + +**WebSocket Socket Manager:** +- Purpose: Prevent duplicate connections, manage subscriptions +- Location: `client/src/shared/hooks/use-character-socket.ts` +- Pattern: Global socket singleton with ref counting +- Features: Auto-reconnect, polling fallback, room subscription + +**Character/Battle Update Types:** +- Purpose: Type-safe event dispatch +- Location: + - `server/src/modules/characters/characters.gateway.ts` (CharacterUpdatePayload) + - `server/src/modules/battle/battle.gateway.ts` (BattleUpdatePayload) +- Pattern: Union types for event kind discrimination + +## Entry Points + +**Server Entry:** +- Location: `server/src/main.ts` +- Triggers: `npm run start:dev` or deployed container startup +- Responsibilities: + - Create NestJS app from AppModule + - Enable global pipes (ValidationPipe) + - Configure CORS from env + - Setup Swagger API docs at `/api/docs` + - Listen on PORT (default 5000) + +**Client Entry:** +- Location: `client/src/main.tsx` +- Triggers: Browser page load or `npm run dev` +- Responsibilities: + - Render React app into #root DOM element + - Wrap with StrictMode + +**Router:** +- Location: `client/src/App.tsx` +- Pattern: React Router v6 with protected routes +- Flow: + 1. QueryClientProvider (React Query setup) + 2. BrowserRouter (React Router) + 3. AppContent checks auth state + 4. Public routes: /login, /register + 5. Protected routes: / (campaigns), /campaigns/:id, /campaigns/:id/characters/:characterId, /campaigns/:id/battle, /campaigns/:id/library + 6. ProtectedRoute wrapper ensures authentication + +## Error Handling + +**Strategy:** Structured error responses with HTTP status codes + +**Backend Patterns:** +- `NotFoundException` - 404 when entity not found +- `ForbiddenException` - 403 when user lacks permission +- `BadRequestException` - 400 for invalid input +- `UnauthorizedException` - 401 for auth failures +- Global error filter could be added for consistent formatting +- Service methods validate access before querying: `checkCampaignAccess()`, `checkCharacterAccess()` + +**Frontend Patterns:** +- API client `response.interceptors` catches 401 → redirects to /login +- Components wrapped in error boundaries (future enhancement) +- Failed requests return rejected promises to component + +**WebSocket Patterns:** +- Token verification on connection → disconnect if invalid +- No structured error responses; silent failures with console logging +- Clients auto-reconnect via socket.io configuration + +## Cross-Cutting Concerns + +**Logging:** +- Backend: NestJS Logger class used in services/gateways +- Frontend: Console.log for development (socket.io events log connection state) + +**Validation:** +- Backend: Global ValidationPipe with DTOs (class-validator) +- Frontend: Form validation in components (manual checks in modals) +- Prisma schema enforces constraints (NOT NULL, unique, enums) + +**Authentication:** +- Backend: JwtAuthGuard applied globally in app.module.ts +- Endpoints opt-out via @Public() decorator +- WebSocket: Token verified in gateway.handleConnection() +- Frontend: Token stored in localStorage (persistent) or sessionStorage (session-only) + +**Authorization:** +- Backend: RolesGuard checks @Roles() metadata +- Service methods verify campaign/character ownership before allowing operations +- Pattern: Check campaign membership → check character ownership → allow operation + +**Real-Time Sync:** +- WebSocket Gateway manages rooms (one room per character/session) +- Clients join room on component mount, leave on unmount +- Broadcasts to all clients in room except sender (optional) + +--- + +*Architecture analysis: 2026-04-27* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 0000000..def9cfa --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -0,0 +1,509 @@ +# Codebase Concerns + +**Analysis Date:** 2026-04-27 + +## Tech Debt + +### Debug Console Logging in Production Code + +**Issue:** Production code contains debug console.log statements that should be removed before deployment + +**Files:** +- `server/src/modules/characters/characters.service.ts` (4 console.log statements in updateHp method, lines 263, 283, 303, 310) +- `client/src/features/battle/hooks/use-battle-socket.ts` (3 console.log statements) +- `client/src/features/characters/components/alchemy-tab.tsx` (3 console.log statements) +- `client/src/features/characters/components/character-sheet-page.tsx` (1 console.log statement) +- `client/src/shared/hooks/use-character-socket.ts` (4 console.log statements) + +**Impact:** Exposes internal logic and data in browser/server console. Can leak sensitive information and degrade performance in high-volume operations. + +**Fix approach:** Implement proper structured logging (Winston, Pino, or similar) for development vs. production environments. Remove or replace all console.log statements with conditional logging. + +--- + +### N+1 Query Problem in Character Details Endpoint + +**Issue:** The findOne method in CharactersService performs multiple sequential async API calls for translation enrichment, causing N+1 query pattern + +**Files:** `server/src/modules/characters/characters.service.ts` (lines 186-222) + +**Problem Details:** +- Fetches items, formulas, and preparedItems from database (3 queries) +- For EACH item, formula, and prepared item, calls `enrichEquipmentWithTranslations()` which queries the Translations table or Claude API +- If a character has 50 items, this results in 50+ additional Prisma queries (or Claude API calls) + +**Impact:** Dramatically slow character sheet loading. Creates rate-limiting pressure on Claude API. Potential timeout on high-inventory characters. + +**Fix approach:** +1. Use Prisma's `include` to load equipment relationships in the initial query +2. Batch translation lookups: collect all unique equipment IDs, translate in one batch, then merge results +3. Consider caching translations at the service layer for repeated requests + +--- + +### Oversized Service Class + +**Issue:** CharactersService is 1,454 lines - violates single responsibility principle and makes testing difficult + +**Files:** `server/src/modules/characters/characters.service.ts` + +**Contains:** +- Character CRUD operations +- HP management with dying/wounded condition logic +- Item management +- Condition management +- Resource management +- Rest system logic +- Alchemy system logic (vials, formulas, prepared items) +- Multiple helper methods (getDyingConditions, getDeathThreshold, etc.) + +**Impact:** Hard to locate code, difficult to test individual features, increased risk of bugs when modifying one feature affecting another. + +**Fix approach:** Split into multiple focused services: +- `CharactersCoreService` - CRUD operations +- `CharacterHpService` - HP and dying/wounded logic +- `CharacterAlchemyService` - Alchemy system +- `CharacterRestService` - Rest and recovery logic + +--- + +## Known Issues + +### Missing Level-Up System + +**Issue:** Level progression is mentioned in CLAUDE.md as "Noch zu implementieren" but no implementation exists + +**Files:** No implementation - feature completely absent + +**Impact:** Cannot advance character levels, blocking career progression mechanic. + +**Workaround:** Level can be updated via direct database manipulation or API patching, but no UI or validation logic. + +**Priority:** High - Critical gameplay feature + +--- + +### Unused equipmentlevel.json File at Project Root + +**Issue:** File `equipmentlevel.json` (250KB+) exists at project root but is not referenced by any code + +**Files:** `/c/Users/ZielonkaA/Documents/Dimension-47/equipmentlevel.json` + +**Impact:** +- Takes up disk space +- Ignored by git, clogs project root +- Suggests abandoned equipment import/level-mapping system +- Unknown purpose - may be partial/corrupted data + +**Fix approach:** +1. Determine original purpose (equipment level mapping?) +2. Either integrate into equipment seed system or delete +3. Check git history to understand why it was abandoned + +--- + +## Security Considerations + +### WebSocket Gateway Authentication is Minimal + +**Issue:** Token validation in WebSocket connection handler is basic but lacks some best practices + +**Files:** `server/src/modules/characters/characters.gateway.ts` (lines 60-93) + +**Current implementation:** +- Extracts token from `client.handshake.auth.token` or Authorization header +- Verifies token signature with JWT_SECRET +- Verifies user exists in database +- No rate limiting on connection attempts +- No IP whitelisting or additional authentication layers + +**Potential risks:** +- Rapid connection attempts could brute-force token validation or create connection storms +- No tracking of legitimate vs. malicious connection attempts +- Token rotation/revocation not supported (token valid until expiry) + +**Recommendations:** +1. Implement connection rate limiting per IP/user +2. Add token revocation mechanism +3. Consider requiring additional re-authentication for sensitive operations +4. Log suspicious connection patterns + +--- + +### Input Validation on Equipment Search Query + +**Issue:** Equipment search endpoint accepts query parameters without upper bounds, potentially enabling resource exhaustion + +**Files:** `server/src/modules/equipment/equipment.controller.ts` (lines 23-42) + +**Current implementation:** +- Takes `limit` parameter (defaults to 50, user-configurable) +- No maximum limit enforced +- Accepts large pagination page numbers +- `traits` parameter is comma-separated without validation on count + +**Potential risks:** +- `?limit=999999999` could cause OOM or timeout +- `?page=999999999999` could overflow internal calculations +- Unbounded trait queries could cause DoS + +**Fix approach:** +1. Set hard maximum limits: `limit` ≤ 500, `page` ≤ 10000 +2. Validate and cap traits array length ≤ 50 +3. Implement pagination cursor-based approach instead of offset-based +4. Add rate limiting per user/IP on search endpoint + +--- + +### CORS Configuration Overly Permissive in Development + +**Issue:** Development CORS allows all localhost origins, which is standard but could leak requests to unintended local services + +**Files:** `server/src/main.ts` (lines 26-47) + +**Current implementation:** +```typescript +if (nodeEnv === 'development' && /^https?:\/\/localhost(:\d+)?$/.test(origin)) { + return callback(null, true); +} +``` + +**Impact:** Any localhost service can make cross-origin requests to the API. Low risk in pure development, but could leak credentials or data if other services run on localhost. + +**Fix approach:** In development, explicitly list expected origins (e.g., `http://localhost:5173`) rather than wildcard localhost. + +--- + +## Performance Bottlenecks + +### Equipment Database with 5,482 Items - Unindexed Search + +**Issue:** Full-text search on Equipment table lacks proper indexing for performance + +**Files:** +- `server/src/modules/equipment/equipment.service.ts` (search implementation) +- `server/prisma/schema.prisma` (Equipment model definition) +- `server/prisma/seed-equipment.ts` (5,482 items) + +**Current queries use:** +- `.contains()` filters on `name`, `summary`, `effect` fields +- No full-text search index +- No composite indexes for common filter combinations + +**Impact:** +- Search queries on large equipment set may be slow +- If multiple users search simultaneously, database load spikes +- Trait filtering requires collection of all results then filtering in application code (N+1 pattern with filters) + +**Improvement path:** +1. Add PostgreSQL `@@@` full-text search with tsvector indexes +2. Add composite indexes: `(category, itemCategory)`, `(level, name)` +3. Use Prisma's `findRaw` for complex searches with proper SQL optimization +4. Consider search caching with Redis for common queries + +--- + +### Translation Caching at Item Retrieval Time + +**Issue:** Translations are fetched on-demand during character detail retrieval, causing repeated Claude API calls for same items + +**Files:** `server/src/modules/characters/characters.service.ts` (lines 126-147, 186-222) + +**Current behavior:** +- First time a character's item is viewed: cache miss, Claude API called +- If multiple characters have same item: Claude API called multiple times +- No cache invalidation strategy or TTL + +**Impact:** +- Increased Claude API costs and latency +- Rate limiting pressure if many characters queried +- Slow character sheet loads + +**Fix approach:** +1. Pre-cache translations for common items at server startup +2. Implement TTL-based cache expiration (e.g., 30 days) +3. Batch missing translations when loading characters with multiple items +4. Cache at equipment level, not at character level + +--- + +## Fragile Areas + +### Character Death/Resurrection State Management + +**Issue:** Dying/Wounded conditions interact with HP changes in complex ways that could have race conditions + +**Files:** `server/src/modules/characters/characters.service.ts` (lines 262-406) + +**Fragile interactions:** +- When HP drops to 0: checks Wounded/Doomed values, calculates Dying value, adds/updates condition, broadcasts update +- When HP increases from 0: removes Dying, adds/increments Wounded, broadcasts update +- Multiple async operations between checking and updating +- No transaction wrapping + +**Risks:** +- If two updates happen simultaneously (WebSocket + rest endpoint), conditions could get corrupted +- Wounded value calculation depends on database state that could change between read and write +- Broadcasting happens after database update, potential sync issues if broadcast fails + +**Safe modification:** +1. Wrap Dying/Wounded logic in Prisma transaction +2. Add optimistic locking (version field) to Character model +3. Consolidate HP update and condition management into single atomic operation +4. Test scenarios: multiple simultaneous HP changes, network race conditions + +**Test coverage:** Currently untested (no test files in server codebase) + +--- + +### Alchemy System State Consistency + +**Issue:** Alchemy state (vials, formulas, prepared items) spans multiple database models with complex interdependencies + +**Files:** +- `server/src/modules/characters/characters.service.ts` (alchemy methods ~200+ lines) +- `server/prisma/schema.prisma` (CharacterAlchemyState, CharacterFormula, CharacterPreparedItem, CharacterResource) + +**Fragile points:** +- Prepared items created by `createPreparedItem()` with `isInfused: true`, then deleted by rest system +- Versatile vials count stored in CharacterAlchemyState but actual vials tracked in CharacterResource +- Formula learning updates both CharacterFormula and CharacterResource +- No constraint ensuring consistency between these tables + +**Risk:** Orphaned records, inconsistent vial counts, invalid formula states + +**Safe modification:** +1. Add unique constraint validation in service methods +2. Use Prisma transactions for multi-table alchemy operations +3. Add data integrity checks in rest system +4. Create migration to validate existing data + +--- + +## Test Coverage Gaps + +### No Backend Tests + +**Issue:** Server codebase has zero test files - all business logic untested + +**Files:** +- `server/src/` directory contains no `.test.ts` or `.spec.ts` files +- Test infrastructure exists (Jest, @nestjs/testing configured) but unused + +**Untested functionality:** +- Authentication and authorization guards +- Character CRUD with access control +- HP updates with Dying/Wounded condition logic (most critical) +- Rest system with conditional resource resets +- Alchemy system with formula learning and vial tracking +- WebSocket message handlers (race conditions, auth) +- Equipment search with filtering + +**Priority areas for testing:** +1. Character death/resurrection state transitions (HIGH - bug risk) +2. Authentication guards on protected endpoints (HIGH - security) +3. Access control: ensure players can't edit others' characters (HIGH - security) +4. Rest system calculations with various ability scores (MEDIUM) +5. Alchemy vial/formula consistency (MEDIUM) + +**Fix approach:** +1. Create test files for each module: `auth.service.spec.ts`, `characters.service.spec.ts`, etc. +2. Start with authentication and authorization tests (security-critical) +3. Add tests for HP/Dying state transitions (highest bug risk) +4. Use Prisma's test database pattern for integration tests + +--- + +### No Frontend Tests + +**Issue:** Client codebase has zero test files - no component or hook testing + +**Files:** +- `client/src/` directory contains no `.test.tsx` or `.spec.tsx` files +- Test infrastructure missing (no Jest/Vitest config, no testing-library) + +**Untested components:** +- Character sheet tabs (Inventory, Alchemy, Actions, Talents) +- HP control component with temporary/current/max interactions +- Condition management modal +- Rest modal with preview calculation +- WebSocket hooks for real-time sync +- Item search and filtering UI + +**Impact:** Cannot verify UI correctly reflects data, no regression protection, refactoring is risky + +**Fix approach:** +1. Install Vitest + React Testing Library +2. Create test files for critical components: HP control, Rest modal, Item search +3. Test WebSocket connection/disconnection handling +4. Add snapshot tests for data-heavy components (character sheet) + +--- + +## Missing Critical Features + +### Level-Up System + +**Issue:** Character advancement is incomplete + +**Problem:** +- Level field exists in database and UI, but no level-up mechanics +- Cannot allocate ability score increases at certain levels +- Cannot select new feats when leveling +- Cannot add/remove skills when leveling + +**Files:** No implementation exists + +**Blocks:** Full character progression gameplay + +--- + +## Dependencies at Risk + +### Claude API Integration as Critical Path + +**Issue:** German translation system depends on Claude API for on-demand translation calls + +**Files:** +- `server/src/modules/claude/claude.service.ts` +- `server/src/modules/translations/translations.service.ts` + +**Risk points:** +1. No fallback if API is unavailable - character sheets fail to load with translations +2. Rate limiting: high-traffic scenarios could hit Claude API limits +3. Cost: unbounded - every unique item/feat/spell triggers an API call +4. Latency: API calls on the critical path of character sheet loading (3+ second potential delay) + +**Migration plan:** +1. Pre-populate translations for all 5,482 equipment items on server startup +2. Batch translate feats/spells during seeding +3. Use Claude API only for user-created custom items +4. Cache everything in database with TTL +5. Implement graceful degradation: show English names if translation fails + +--- + +### Prisma 7.2.0 - Check for Security/Critical Updates + +**Files:** +- `server/package.json` - `@prisma/client: ^7.2.0` +- `server/prisma/schema.prisma` - Using latest client + +**Note:** Version is recent (January 2025+) but "^" allows minor updates. Monitor for critical patches in Prisma security advisories. + +--- + +## Scaling Limits + +### Equipment Search at 5,482 items + +**Current capacity:** Linear search with `.contains()` filters + +**Limit:** Performance degrades beyond ~10,000 items, potential timeout at 50,000+ + +**Scaling path:** +1. Add PostgreSQL full-text search indexes (supports millions of items) +2. Implement search result caching +3. Consider Elasticsearch if full-text becomes bottleneck +4. Pagination already uses `take/skip`, good foundation + +--- + +### WebSocket Connections per Campaign + +**Issue:** No connection pooling limits, no per-campaign connection quota + +**Files:** `server/src/modules/characters/characters.gateway.ts` + +**Current behavior:** +- Each connection stores socket in `connectedClients` Map +- Broadcasts go to all sockets in character room +- No limits on concurrent connections per campaign or globally + +**Potential issue:** If campaign has 100 characters and each has 10 connected users watching, that's 1,000 broadcast operations per update. At scale this causes CPU/memory pressure. + +**Scaling recommendations:** +1. Set max connections per campaign (e.g., 500) +2. Implement connection pooling/groups (watch list vs. active participants) +3. Use socket.io's built-in room management more aggressively +4. Monitor memory usage of `connectedClients` Map for leaks + +--- + +## Database Schema Concerns + +### Character pathbuilderData Stored as JSON Blob + +**Issue:** Original Pathbuilder export is stored unstructured as JSON + +**Files:** `server/prisma/schema.prisma` (line 198: `pathbuilderData Json?`) + +**Potential issues:** +- Cannot query Pathbuilder fields directly +- Data validation relies on application code, not schema +- Difficult to report or audit what data came from imports +- No versioning if Pathbuilder export format changes + +**Impact:** Low - used primarily for reference, but limits data flexibility + +**Improvement path:** +1. Parse and normalize Pathbuilder data into proper database fields +2. Consider storing hash of original for audit trail +3. Add migration to extract key fields from JSON for querying + +--- + +## Environmental Configuration + +### JWT_SECRET Management + +**Issue:** JWT_SECRET passed via environment variable with no rotation mechanism + +**Files:** +- `server/src/modules/auth/strategies/jwt.strategy.ts` +- `server/src/modules/characters/characters.gateway.ts` + +**Concern:** +- If secret is leaked, all existing tokens become invalid +- No way to rotate secret without invalidating all sessions +- No secret versioning + +**Recommendation:** +1. Implement secret versioning in token header +2. Support multiple valid secrets during rotation period +3. Add secret rotation schedule (e.g., quarterly) +4. Store secrets in vaulting system, not .env + +--- + +### ENV File Example Status + +**Files:** +- `server/.env.example` +- `client/.env.example` + +**Status:** Confirmed present in CLAUDE.md - properly structured + +**Verification:** Users can reference templates when setting up environment + +--- + +## API Documentation + +### Swagger API Docs Enabled but may Expose Sensitive Operations + +**Files:** `server/src/main.ts` (lines 49-64) + +**Current state:** +- Swagger UI enabled at `/api/docs` +- Available to all users with API access +- Could expose internal API structure to unauthorized users + +**Recommendation:** +- In production: require ADMIN role to view Swagger +- In development: keep open for debugging +- Add basic authentication to `/api/docs` endpoint + +--- + +*Concerns audit: 2026-04-27* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 0000000..00880f7 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -0,0 +1,221 @@ +# Coding Conventions + +**Analysis Date:** 2026-04-27 + +## Naming Patterns + +**Files:** +- Components: kebab-case (e.g., `character-sheet-page.tsx`, `add-condition-modal.tsx`, `hp-control.tsx`) +- Utilities/Services: kebab-case (e.g., `use-character-socket.ts`, `export-character-html.ts`) +- Directories: kebab-case (e.g., `features/`, `shared/`, `components/`) + +**Functions:** +- camelCase for all function names +- Hooks prefixed with `use` (e.g., `useAuthStore`, `useCharacterSocket`, `useMemo`, `useState`) +- Event handlers prefixed with `handle` (e.g., `handleApply`, `handleConnection`, `handleDisconnect`) +- Getter/setter methods follow camelCase (e.g., `getToken()`, `setToken()`) + +**Variables:** +- camelCase for all variable names +- Constants in UPPER_SNAKE_CASE (e.g., `PROFICIENCY_BONUS`, `SKILL_DATA`, `TABS`) +- Type unions with capitalized names (e.g., `CharacterType`, `AbilityType`, `TabType`) +- Boolean prefixes: `is`, `has`, `should`, `can` (e.g., `isLoading`, `isOwner`, `hasAccess`) + +**Types:** +- PascalCase for all types, interfaces, and enums +- Domain types in shared: `Character`, `Campaign`, `User`, `CharacterItem`, `CharacterFeat` +- Props interfaces suffixed with `Props` (e.g., `HpControlProps`, `AddConditionModalProps`) +- DTO interfaces suffixed with `Dto` (e.g., `CreateCharacterDto`, `LoginDto`, `RegisterDto`) +- State types in Zustand: domain-specific (e.g., `AuthState`) + +## Code Style + +**Formatting:** +- Prettier configured in `server/.prettierrc` with: + - Single quotes (`singleQuote: true`) + - Trailing commas on all (`trailingComma: "all"`) +- Line length: implicit (Prettier default ~80 chars but allows overflow) +- Indentation: 2 spaces + +**Linting:** +- Client: ESLint 9 with flat config (`eslint.config.js`) + - Uses TypeScript ESLint recommended rules + - React Hooks plugin with recommended rules + - React Refresh plugin for Vite + - No custom strict rules beyond defaults +- Server: ESLint 9 with flat config (`eslint.config.mjs`) + - Uses TypeScript ESLint recommended rules with type checking (`recommendedTypeChecked`) + - Prettier plugin for code formatting integration + - **Important:** `@typescript-eslint/no-explicit-any` is **turned OFF** (line 29) + - Warnings (not errors) for: `no-floating-promises`, `no-unsafe-argument` + - Custom Prettier rule with `endOfLine: "auto"` for Windows compatibility + +**TypeScript:** +- **Strict Mode Enforced** on client (`client/tsconfig.app.json`): + - `"strict": true` ✓ + - `"noUnusedLocals": true` ✓ + - `"noUnusedParameters": true` ✓ + - `"noFallthroughCasesInSwitch": true` ✓ + - `"noUncheckedSideEffectImports": true` ✓ +- Server TypeScript (`server/tsconfig.json`): + - `"strictNullChecks": true` ✓ + - `"forceConsistentCasingInFileNames": true` ✓ + - `"noImplicitAny": false` (allows any, soft enforcement) + - `"skipLibCheck": true` (skips lib type checking) + +## Import Organization + +**Order:** (observed pattern) +1. React/Framework imports (`react`, `react-router-dom`) +2. Third-party utilities (`zustand`, `axios`, `socket.io-client`) +3. NestJS/Framework imports (`@nestjs/*`) +4. Type-only imports (`type { ... }`) +5. Relative imports from shared (`@/shared/...`, `@/features/...`) +6. Decorators and metadata (after other imports) + +**Path Aliases:** +- Client: `@/*` → `./src/*` (defined in `vite.config.ts` and `tsconfig.app.json`) +- Server: No path alias configured; uses relative paths +- Always use `@/` prefix on client to avoid relative path hell + +**Module exports:** +- Barrel files used: `features/*/index.ts`, `shared/components/ui/index.ts` +- Default exports on pages: `export default App` +- Named exports for utilities, types, hooks: `export { HpControl }`, `export const api = new ApiClient()` + +## Error Handling + +**Philosophy (per CLAUDE.md):** No shortcuts. Proper error handling, not try/catch with console.log. + +**Server-side (NestJS):** +- Use NestJS exception classes: `NotFoundException`, `ForbiddenException`, `UnauthorizedException`, `ConflictException` +- Access checks before operations (e.g., `checkCampaignAccess()`, `checkCharacterAccess()` in `characters.service.ts`) +- Return meaningful error messages: "Campaign not found", "No access to this campaign" +- Prisma operations wrapped in try-catch only when necessary for data validation +- JWT verification throws exceptions through guard: `JwtAuthGuard` handles 401s + +**Client-side (React):** +- Axios interceptors for request/response handling (in `api.ts`) +- 401 errors trigger logout and redirect to `/login` (except auth endpoints) +- Error state in components: `isLoading`, `error`, `pendingChange` states +- Modal operations: try/catch with `setIsLoading(false)` in finally block +- Error logging: `console.error()` for debugging, no silent failures +- User feedback: Navigation on error (`navigate()`) or via error state + +**Socket.io (WebSocket Gateway):** +- Token validation on connection (throws `client.disconnect()`) +- Logger for all connection/disconnection events +- Errors logged but don't crash server + +## Logging + +**Framework:** +- Server: NestJS `Logger` (in modules/gateways) + - E.g., `private logger = new Logger('CharactersGateway')` + - Used for connection events, warnings, errors +- Client: `console.error()` for error debugging + +**Patterns:** +- Server logs connection events with user context +- Client silently catches most errors, logs critical ones +- No debug logging infrastructure in place + +## Comments + +**When to Comment:** +- Constants with unclear meaning (e.g., `PROFICIENCY_BONUS` values, skill-to-ability mappings) +- Complex calculations (e.g., HP percentage calculations) +- Business logic that isn't immediately obvious (e.g., Pathfinder 2e rules) +- NOT used for obvious code ("increment counter") + +**JSDoc/TSDoc:** +- Minimal usage observed +- API methods documented with Swagger decorators on server: `@ApiOperation()`, `@ApiResponse()` +- No runtime JSDoc comments in source + +## Function Design + +**Size:** +- Modal components: 500-1700 lines (large, but feature-complete) +- Service methods: 10-50 lines (concise, focused) +- Utility functions: 5-20 lines + +**Parameters:** +- Interface-based: Pass objects instead of multiple params + - E.g., `onAdd(condition: { name: string; nameGerman: string; value?: number })` + - DTO pattern on server: `CreateCharacterDto`, `LoginDto` +- Optional params with defaults: `remember: boolean = false` + +**Return Values:** +- Async functions return typed Promises: `async getCharacter(): Promise` +- Component callbacks: callbacks are async promises (`onHpChange: (newHp: number) => Promise`) +- Services return full domain objects with relations included + +## Module Design + +**Exports:** +- Components export as named: `export function HpControl() { ... }` +- Utilities export as named: `export const api = new ApiClient()` +- One export per file (generally) + +**Barrel Files:** +- Used in features: `features/auth/index.ts` re-exports `useAuthStore`, `LoginPage`, `RegisterPage` +- Used for UI components: `shared/components/ui/index.ts` re-exports all buttons, cards, inputs +- Reduces import complexity + +**Dependency Injection (Server):** +- NestJS @Injectable() decorator for services +- Constructor injection: `constructor(private prisma: PrismaService)` +- Circular dependencies resolved with @Inject(forwardRef()): seen in `CharactersService` + +## State Management + +**Client (Zustand):** +- Single store pattern: `create()` +- Persisted state with middleware: `persist()` +- Partialize: stores only non-sensitive fields (`user`, `isAuthenticated`) +- Actions as methods in store: `login()`, `logout()`, `checkAuth()` +- Error state in store: `error: string | null` + +**Server (NestJS):** +- No client state management needed +- WebSocket gateway maintains connection map: `connectedClients = new Map()` + +## Design Tokens (UI) + +**Colors:** +- Primary: Magenta `#c26dbc` (in Tailwind as `primary-500`, `primary-600`, etc.) +- Secondary: Dark grays for dark mode (`secondary-800`, `secondary-700`) +- Text: Primary (light), secondary (dimmer), tertiary (dimmest) +- Background: Primary, secondary, tertiary layers +- Error: Red (`error-500`, `error-600`) +- Success: Green (`green-500`) +- Warning: Yellow (`yellow-500`) + +**Typography:** +- Font sizes: sm, base, lg (Tailwind defaults) +- Weights: normal, medium (600), semibold +- All UI text in German except code/technical terms + +**Icons:** +- Lucide React exclusively (e.g., `Heart`, `Swords`, `BookOpen`, `Package`) +- No emoji anywhere +- Icon sizes: `h-4 w-4` (standard), `h-6 w-6` (large) + +**Touch Targets:** +- Minimum 44px (mobile-first, accessible) +- Buttons: `h-11` (44px), `h-9` (36px for small), `h-12` (48px for large) +- Icon buttons: `h-11 w-11` (44x44px) + +**Spacing:** +- Tailwind v4 defaults: `p-4`, `gap-2`, `rounded-lg`, `rounded-2xl` +- Modals: `rounded-t-2xl sm:rounded-2xl` (bottom sheet on mobile, centered on desktop) + +**Responsive:** +- Mobile-first breakpoints: `sm:` prefix for tablet+ (e.g., `sm:items-center`, `sm:rounded-2xl`) +- Flexbox for layouts: `flex`, `flex-col`, `items-center`, `justify-between` +- Grid rarely used + +--- + +*Convention analysis: 2026-04-27* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 0000000..8bc7d79 --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -0,0 +1,226 @@ +# External Integrations + +**Analysis Date:** 2026-04-27 + +## APIs & External Services + +**Anthropic Claude API:** +- Service: Claude API for real-time translation of game content to German +- What it's used for: + - On-demand translation of feats, equipment, spells, items, conditions, and other PF2e content + - Translates item effect text for alchemical items + - Batches translations in groups for efficiency + - Results cached in `Translation` table with quality ratings (HIGH/MEDIUM/LOW) + - Fallback: System returns untranslated English names if API key not configured + - Model: `claude-haiku-4-5-20251001` (lightweight model for cost-effective translations) + +- SDK/Client: `@anthropic-ai/sdk` 0.71.2 +- Auth: Environment variable `ANTHROPIC_API_KEY` +- Implementation: `server/src/modules/claude/claude.service.ts` +- Module: `server/src/modules/claude/claude.module.ts` +- Usage: Called by `server/src/modules/translations/translations.service.ts` + +## Data Storage + +**Databases:** +- PostgreSQL 16 (via Docker Compose or production instance) + - Connection: Environment variable `DATABASE_URL` + - Client: Prisma ORM 7.2.0 with PostgreSQL adapter + - Schema: `server/prisma/schema.prisma` (25 models covering users, campaigns, characters, equipment, alchemy, battle, documents) + - Migrations: Version-controlled in `server/prisma/migrations/` + - Seeding: `server/prisma/seed.ts` (base data), `server/prisma/seed-equipment.ts` (5,482 PF2e items), `server/prisma/seed-feats.ts` + +**File Storage:** +- Local filesystem only (currently) + - Directory: Configured via `UPLOAD_DIR` env var (default `./uploads`) + - Served via NestJS `ServeStaticModule` at `/uploads` endpoint + - Max file size: `MAX_FILE_SIZE` env var (default 10MB) + - Types supported: HTML, PDF (from schema `Document` model) + +**Caching:** +- In-memory client state via Zustand (auth store): `client/src/features/auth/hooks/use-auth-store.ts` +- Server-side caching via TanStack React Query: Automatic cache of API responses with configurable stale times +- Translation cache in `Translation` table with `@@unique([type, englishName])` constraint +- No Redis or external cache layer currently + +## Authentication & Identity + +**Auth Provider:** +- Custom JWT-based authentication (no external OAuth provider) + - Implementation: `server/src/modules/auth/auth.service.ts` + - Strategy: Passport.js with JWT strategy (`server/src/modules/auth/strategies/jwt.strategy.ts`) + +**Authentication Flow:** +1. Users register with username, email, password via `POST /api/auth/register` +2. Passwords hashed with bcrypt (salt rounds: 10, configurable) +3. Login returns JWT token valid for `JWT_EXPIRES_IN` (default "7d") +4. Client stores token in localStorage (persistent) or sessionStorage (session-only) + - Determined by "Remember me" checkbox during login + - Implementation: `client/src/shared/lib/api.ts` +5. Token attached to all subsequent requests via Authorization header +6. Token verified on WebSocket connection via `characters.gateway.ts` and `battle.gateway.ts` + +**User Roles:** +- ADMIN - Full system access +- GM - Game Master, can manage campaigns, create NPCs, control battles +- PLAYER - Standard user, can own characters + +**Guards:** +- Global JWT auth guard: `server/src/modules/auth/guards/jwt-auth.guard.ts` +- Role-based guard: `server/src/modules/auth/guards/roles.guard.ts` + +## Monitoring & Observability + +**Error Tracking:** +- None (no Sentry, Rollbar, or external service configured) +- Server logs to console via NestJS Logger +- Client console errors only + +**Logs:** +- Server: NestJS Logger (console output in development, can be piped to file in production) +- Client: Browser console only +- No centralized logging infrastructure + +## CI/CD & Deployment + +**Hosting:** +- Not configured (manual deployment expected) +- Docker Compose available for local development + +**CI Pipeline:** +- None detected +- ESLint, Prettier, and tests are run locally before commit + +## WebSockets & Real-Time Communication + +**Transport:** +- Socket.io 4.8.3 +- Client library: `socket.io-client` 4.8.3 +- Server: NestJS WebSocket gateways with Socket.io adapter + +**Character Updates (Real-Time Sync):** +- Gateway: `server/src/modules/characters/characters.gateway.ts` +- Namespace: `/characters` +- CORS configured from environment variable `CORS_ORIGINS` +- Authentication: JWT token verified on connection +- Update types: + - `hp` - Health points (current, temp, max) + - `conditions` - Add/remove/update character conditions + - `item` - Add/remove inventory items + - `inventory` - Bulk inventory updates + - `money` - Update credits/currency + - `level` - Character level changes + - `equipment_status` - Mark items as equipped/unequipped/invested + - `rest` - Rest cycle triggers (HP restore, condition cleanup) + - `alchemy_vials` - Update alchemist versatile vials + - `alchemy_formulas` - Add/remove formulae + - `alchemy_prepared` - Update prepared alchemical items + - `alchemy_state` - Update research field and advanced alchemy state + - `dying` - Dying and recovery state + +**Battle Updates (Real-Time Sync):** +- Gateway: `server/src/modules/battle/battle.gateway.ts` +- Update types: + - `token_added` - New combatant token on map + - `token_removed` - Token removed + - `token_moved` - Position changed + - `token_updated` - Stat/property updates + - `token_hp_changed` - HP/damage tracking + - `initiative_set` - Initiative order + - `round_advanced` - Round counter + - `session_updated` - Session state + - `session_deleted` - Battle session ended + +**Client Hooks:** +- `client/src/features/characters/hooks/use-character-socket.ts` - Character update subscriptions +- `client/src/features/battle/hooks/use-battle-socket.ts` - Battle update subscriptions +- Singleton socket pattern to prevent multiple connections +- Reference counting for cleanup on unmount + +## Pathbuilder Integration + +**Import Format:** +- Reads JSON export from Pathbuilder 2e character builder +- Stored as raw JSON blob in `Character.pathbuilderData` field +- Used to populate character stats, feats, skills, spells on initial import + +**Data Mapping:** +- Character abilities (STR, DEX, CON, INT, WIS, CHA) +- Skills with proficiency levels +- Feats with sources (Class, Ancestry, General, Skill, Bonus, Archetype) +- Spells with traditions and prepared status +- Equipment and inventory +- Alchemy (if applicable) + +**Endpoint:** +- `POST /api/characters/import-pathbuilder` - Import character from Pathbuilder JSON + +## Equipment Database + +**Data Source:** +- 5,482 Pathfinder 2e equipment items imported from JSON +- Seed script: `server/prisma/seed-equipment.ts` +- Source: JSON data files in `server/prisma/data/` + +**Categories:** +- Weapons (with damage, range, properties) +- Armor (with AC, penalties, strength requirements) +- Shields (with HP, hardness, broken threshold) +- Consumables (potions, scrolls, talismans, etc.) +- General equipment + +**API Endpoints:** +- `GET /api/equipment` - Search/list equipment +- `GET /api/equipment/categories` - List available categories +- `GET /api/equipment/weapons` - Weapon-specific query +- `GET /api/equipment/armor` - Armor-specific query + +## External References + +**Archon Omni Documentation (AONPRD):** +- Links stored in `Feat.url`, `Equipment.url`, `Spell.url`, `Trait.url` +- Frontend links to official Pathfinder 2e documentation +- No direct API integration, read-only external links + +## Environment Configuration + +**Required Environment Variables:** + +Server (`server/.env`): +- `DATABASE_URL` - PostgreSQL connection string (e.g., `postgresql://user:pass@localhost:5432/dimension47?schema=public`) +- `JWT_SECRET` - Secret key for signing JWT tokens (minimum 32 characters recommended) +- `JWT_EXPIRES_IN` - Token expiration (default "7d") +- `PORT` - Server listen port (default 5000) +- `NODE_ENV` - Environment mode (development/production) +- `CORS_ORIGINS` - Comma-separated allowed origins (e.g., `http://localhost:5173,http://localhost:3000`) +- `ANTHROPIC_API_KEY` - Claude API key (optional, disables translations if missing) +- `UPLOAD_DIR` - File upload directory (default `./uploads`) +- `MAX_FILE_SIZE` - Max upload size in bytes (default 10485760 = 10MB) + +Client (`client/.env`): +- `VITE_API_URL` - Backend API URL (e.g., `http://localhost:5000/api`) + +**Secrets Location:** +- `.env` files (NOT committed to git, see `.gitignore`) +- Template files: `server/.env.example` and `client/.env.example` +- Developers copy examples to `.env` and fill in secrets + +## Webhooks & Callbacks + +**Incoming:** +- None configured + +**Outgoing:** +- None configured + +## Cross-Domain Communication + +**CORS Policy:** +- Configured via `CORS_ORIGINS` environment variable +- Applied to HTTP endpoints and WebSocket connections +- Development allows all `localhost:*` origins +- Production requires explicit whitelist + +--- + +*Integration audit: 2026-04-27* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 0000000..d5ec201 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -0,0 +1,164 @@ +# Technology Stack + +**Analysis Date:** 2026-04-27 + +## Languages + +**Primary:** +- TypeScript 5.9.3 (server), 5.7.3 (client) - Strict mode enabled, full type safety across codebase + +**Secondary:** +- JavaScript (build outputs, scripts) +- HTML/CSS (frontend templates) + +## Runtime + +**Environment:** +- Node.js (version not pinned, assumed LTS) - Used for both server and client dev/build +- ES2023 target (server), ES2022 target (client) + +**Package Manager:** +- npm - Lockfiles present for both client and server + +## Frameworks + +**Core Frontend:** +- React 19.2.0 - UI framework +- React Router DOM 7.12.0 - Client-side routing +- Vite 7.2.4 - Build tool and dev server + +**Core Backend:** +- NestJS 11.0.1 - Web framework with TypeScript-first design +- Express (via `@nestjs/platform-express`) - Underlying HTTP server + +**Frontend Styling:** +- Tailwind CSS 4.1.18 - Utility-first CSS framework +- `@tailwindcss/vite` plugin for Vite integration + +**Testing & Development:** +- Jest 30.0.0 - Test runner (server-side only, configured in package.json) +- ts-jest - TypeScript support for Jest +- Supertest 7.0.0 - HTTP assertion library (server integration tests) +- @vitejs/plugin-react - React fast refresh for Vite + +## Key Dependencies + +**Critical Server:** +- `@prisma/client` 7.2.0 - ORM and database abstraction layer +- `prisma` 7.2.0 - CLI and schema management tool +- `@anthropic-ai/sdk` 0.71.2 - Claude API for on-demand German translations +- `socket.io` 4.8.3 - WebSocket library for real-time communication +- `@nestjs/websockets` 11.1.12 - NestJS WebSocket integration +- `@nestjs/platform-socket.io` 11.1.12 - Socket.io adapter for NestJS +- `@nestjs/jwt` 11.0.2 - JWT authentication provider +- `@nestjs/passport` 11.0.5 - Passport.js integration for authentication +- `passport-jwt` 4.0.1 - JWT strategy for Passport +- `bcrypt` 6.0.0 - Password hashing +- `class-validator` 0.14.3 - Request DTO validation +- `class-transformer` 0.5.1 - DTO transformation +- `@nestjs/swagger` 11.2.5 - OpenAPI/Swagger documentation +- `@nestjs/config` 4.0.2 - Environment configuration management +- `dotenv` 17.2.3 - .env file loading + +**Critical Client:** +- `axios` 1.13.2 - HTTP client library for API requests +- `socket.io-client` 4.8.3 - WebSocket client for real-time updates +- `zustand` 5.0.10 - Client state management (auth store) +- `@tanstack/react-query` 5.90.19 - Server state and data fetching (caching, synchronization) +- `react-router-dom` 7.12.0 - Client-side routing +- `framer-motion` 12.26.2 - Animation library +- `lucide-react` 0.562.0 - Icon library (SVG icons) +- `clsx` 2.1.1 - Conditional CSS class utilities +- `tailwind-merge` 3.4.0 - Tailwind class merging utility + +**Development (Backend):** +- `@nestjs/cli` 11.0.0 - NestJS code generation and project scaffolding +- `@nestjs/schematics` 11.0.0 - Code generators for NestJS +- `prettier` 3.4.2 - Code formatter +- `eslint` 9.18.0 - Linting +- `typescript-eslint` 8.20.0 - TypeScript ESLint support +- `tsx` 4.21.0 - TypeScript execution (used for seed scripts) +- `tsconfig-paths` 4.2.0 - TypeScript path alias resolution +- `ts-node` 10.9.2 - TypeScript REPL and script runner +- `ts-loader` 9.5.2 - TypeScript webpack loader + +**Development (Frontend):** +- `eslint` 9.39.1 - Linting +- `typescript-eslint` 8.46.4 - TypeScript ESLint support +- `eslint-plugin-react-hooks` 7.0.1 - React Hooks linting rules +- `eslint-plugin-react-refresh` 0.4.24 - Vite React refresh linting + +## Configuration + +**Environment:** +- Server loads from `.env` file (see `.env.example`): + - `DATABASE_URL` - PostgreSQL connection string + - `JWT_SECRET` - Secret key for signing tokens + - `JWT_EXPIRES_IN` - Token expiration time (default "7d") + - `PORT` - Server port (default 5000) + - `NODE_ENV` - Environment (development/production) + - `CORS_ORIGINS` - Comma-separated list of allowed origins + - `ANTHROPIC_API_KEY` - Claude API key for translations (optional) + - `UPLOAD_DIR` - Directory for file uploads + - `MAX_FILE_SIZE` - Maximum upload file size + +- Client loads from `.env` or `.env.local`: + - `VITE_API_URL` - Backend API base URL (e.g., `http://localhost:5000/api`) + - WebSocket URL derived automatically by removing `/api` suffix + +**TypeScript:** +- Server: `server/tsconfig.json` with target ES2023, decorators enabled + - `strictNullChecks: true` + - `forceConsistentCasingInFileNames: true` + - `noImplicitAny: false` (allows implicit any in some cases) + - Path aliases via `baseUrl: ./` + +- Client: `client/tsconfig.app.json` with target ES2022, strict mode + - `strict: true` - All strict options enabled + - `noUnusedLocals: true`, `noUnusedParameters: true` + - `jsx: react-jsx` - JSX without React import required + - Path alias `@/*` → `./src/*` + +**Build:** +- Client Vite config: `client/vite.config.ts` + - Dev server port 5173 + - API proxy `/api` → `http://localhost:5000` + - React plugin with React 19 support + - Tailwind CSS Vite plugin + - Type checking via `tsc -b` before build + +- Server NestJS config: `server/nest-cli.json` (if exists) + - Build output to `dist/` + +## Platform Requirements + +**Development:** +- Node.js LTS (tested with v22) +- npm (lockfiles version management) +- PostgreSQL 16+ (via Docker Compose) +- Docker & Docker Compose (for database) +- Git + +**Production:** +- Node.js LTS +- PostgreSQL 16+ (managed service or self-hosted) +- Anthropic API key for translations (optional but recommended) +- File storage: Local filesystem or cloud storage (currently local via `./uploads`) + +## Database + +**PostgreSQL 16 Alpine** (via Docker Compose): +- Port 5432 (internal), 5433 (exposed for dev) +- Database name: `dimension47` +- Default credentials in docker-compose (dev only) +- pgAdmin 4 included for database management (port 5050) + +**Prisma Configuration:** +- Schema: `server/prisma/schema.prisma` +- Generator: Prisma Client with CommonJS module format +- Adapter: `@prisma/adapter-pg` for optimized PostgreSQL queries +- Output: `src/generated/prisma/` + +--- + +*Stack analysis: 2026-04-27* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 0000000..8f47291 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -0,0 +1,458 @@ +# Codebase Structure + +**Analysis Date:** 2026-04-27 + +## Directory Layout + +``` +dimension47/ +├── client/ # React 19 + Vite frontend +│ ├── src/ +│ │ ├── main.tsx # React root + DOM render +│ │ ├── App.tsx # Router setup (BrowserRouter, Routes) +│ │ ├── index.css # Global styles (Tailwind v4) +│ │ ├── app/ # App configuration & routing +│ │ ├── features/ # Feature modules (auth, characters, campaigns, etc.) +│ │ │ ├── auth/ # Authentication (login, register, useAuthStore) +│ │ │ │ ├── components/ +│ │ │ │ │ ├── login-page.tsx +│ │ │ │ │ └── register-page.tsx +│ │ │ │ ├── hooks/ +│ │ │ │ │ └── use-auth-store.ts +│ │ │ │ └── index.ts # Barrel export +│ │ │ ├── characters/ # Character sheet & inventory +│ │ │ │ ├── components/ # Modals, tabs, controls +│ │ │ │ │ ├── character-sheet-page.tsx # Main tab container +│ │ │ │ │ ├── actions-tab.tsx # PF2e actions list +│ │ │ │ │ ├── alchemy-tab.tsx # Alchemy system +│ │ │ │ │ ├── add-condition-modal.tsx # Add status effect +│ │ │ │ │ ├── add-item-modal.tsx # Equipment search & add +│ │ │ │ │ ├── add-feat-modal.tsx # Add talent +│ │ │ │ │ ├── feat-detail-modal.tsx # Talent details +│ │ │ │ │ ├── item-detail-modal.tsx # Equipment details +│ │ │ │ │ ├── hp-control.tsx # HP management +│ │ │ │ │ ├── rest-modal.tsx # Rest system +│ │ │ │ │ ├── recovery-check-modal.tsx # Dying state recovery +│ │ │ │ │ ├── dying-indicator.tsx # Dying condition display +│ │ │ │ │ ├── import-character-modal.tsx # Pathbuilder import +│ │ │ │ │ ├── create-character-modal.tsx # New character +│ │ │ │ │ ├── edit-character-modal.tsx # Edit character info +│ │ │ │ │ └── image-crop-modal.tsx # Avatar cropping +│ │ │ │ ├── utils/ +│ │ │ │ │ └── export-character-html.ts # Character sheet HTML export +│ │ │ │ └── index.ts +│ │ │ ├── campaigns/ # Campaign management +│ │ │ │ ├── components/ +│ │ │ │ │ ├── campaigns-page.tsx # Campaign list (home) +│ │ │ │ │ ├── campaign-detail-page.tsx # Campaign overview +│ │ │ │ │ ├── create-campaign-modal.tsx # New campaign +│ │ │ │ │ ├── edit-campaign-modal.tsx # Edit campaign info +│ │ │ │ │ └── add-member-modal.tsx # Add player to campaign +│ │ │ │ └── index.ts +│ │ │ ├── battle/ # Battle screen & real-time combat +│ │ │ │ ├── components/ +│ │ │ │ │ ├── battle-page.tsx # Main battle screen +│ │ │ │ │ ├── battle-canvas.tsx # Map & token rendering +│ │ │ │ │ ├── battle-session-list.tsx # Session selection +│ │ │ │ │ └── token.tsx # Token component +│ │ │ │ ├── hooks/ +│ │ │ │ │ └── use-battle-socket.ts # Battle WebSocket hook +│ │ │ │ └── index.ts +│ │ │ └── library/ # GM library (maps, combatants) +│ │ │ ├── components/ +│ │ │ │ ├── library-page.tsx # Maps & combatants list +│ │ │ │ ├── upload-map-modal.tsx # Add battle map +│ │ │ │ └── create-combatant-modal.tsx # Create NPC template +│ │ │ └── index.ts +│ │ ├── shared/ # Reusable code across features +│ │ │ ├── components/ +│ │ │ │ ├── layout.tsx # App wrapper (navbar, layout) +│ │ │ │ ├── protected-route.tsx # Auth enforcement wrapper +│ │ │ │ └── ui/ # shadcn/ui components +│ │ │ │ ├── button.tsx +│ │ │ │ ├── card.tsx +│ │ │ │ ├── input.tsx +│ │ │ │ ├── spinner.tsx +│ │ │ │ ├── action-icon.tsx +│ │ │ │ └── index.ts +│ │ │ ├── hooks/ +│ │ │ │ ├── use-character-socket.ts # Character real-time sync +│ │ │ │ └── (battle hooks if added) +│ │ │ ├── lib/ +│ │ │ │ ├── api.ts # API client (axios + token mgmt) +│ │ │ │ └── utils.ts # Utility functions +│ │ │ └── types/ +│ │ │ └── index.ts # All TypeScript interfaces & types +│ │ └── assets/ # Images, fonts +│ ├── public/ +│ │ ├── data/ # JSON data files (equipment, etc.) +│ │ └── icons/ # SVG/icon files +│ ├── index.html # HTML template +│ ├── vite.config.ts # Vite configuration +│ ├── tsconfig.json # TypeScript configuration +│ ├── tailwind.config.js # Tailwind CSS v4 config +│ ├── postcss.config.js # PostCSS plugins +│ ├── package.json +│ └── dist/ # Build output (generated) +│ +├── server/ # NestJS backend +│ ├── src/ +│ │ ├── main.ts # Server bootstrap & app setup +│ │ ├── app.module.ts # Root NestJS module (imports all features) +│ │ ├── common/ # Shared utilities across modules +│ │ │ └── decorators/ +│ │ │ ├── current-user.decorator.ts # Extract user from request +│ │ │ ├── public.decorator.ts # Skip auth on endpoints +│ │ │ ├── roles.decorator.ts # Specify required roles +│ │ │ └── index.ts +│ │ ├── prisma/ +│ │ │ └── prisma.service.ts # Database service wrapper +│ │ ├── modules/ # Feature modules (NestJS-style) +│ │ │ ├── auth/ # Authentication & JWT +│ │ │ │ ├── auth.controller.ts # POST /auth/login, /auth/register +│ │ │ │ ├── auth.service.ts # User validation, JWT signing +│ │ │ │ ├── auth.module.ts # Module exports & dependencies +│ │ │ │ ├── guards/ +│ │ │ │ │ ├── jwt-auth.guard.ts # JWT validation (global) +│ │ │ │ │ └── roles.guard.ts # Role checking +│ │ │ │ ├── strategies/ +│ │ │ │ │ └── jwt.strategy.ts # Passport JWT strategy +│ │ │ │ └── dto/ +│ │ │ │ ├── login.dto.ts +│ │ │ │ └── register.dto.ts +│ │ │ ├── campaigns/ # Campaign management +│ │ │ │ ├── campaigns.controller.ts # CRUD campaigns, member management +│ │ │ │ ├── campaigns.service.ts # Campaign operations & access checks +│ │ │ │ ├── campaigns.module.ts +│ │ │ │ └── dto/ +│ │ │ │ └── (campaign DTOs) +│ │ │ ├── characters/ # Character system (core feature) +│ │ │ │ ├── characters.controller.ts # Character CRUD, conditions, items, feats +│ │ │ │ ├── characters.service.ts # Character logic, HP, conditions, inventory +│ │ │ │ ├── characters.gateway.ts # WebSocket real-time sync +│ │ │ │ ├── characters.module.ts +│ │ │ │ ├── pathbuilder-import.service.ts # Pathbuilder JSON parsing +│ │ │ │ ├── alchemy.controller.ts # Alchemy endpoints +│ │ │ │ ├── alchemy.service.ts # Alchemy system logic +│ │ │ │ └── dto/ +│ │ │ │ └── (character, alchemy DTOs) +│ │ │ ├── equipment/ # Equipment database +│ │ │ │ ├── equipment.controller.ts # GET /equipment (search, categories) +│ │ │ │ ├── equipment.service.ts # Equipment search & filtering +│ │ │ │ ├── equipment.module.ts +│ │ │ │ └── dto/ +│ │ │ ├── feats/ # Feat database +│ │ │ │ ├── feats.controller.ts # GET /feats (search) +│ │ │ │ ├── feats.service.ts # Feat queries +│ │ │ │ ├── feats.module.ts +│ │ │ │ └── dto/ +│ │ │ ├── battle/ # Battle system (maps, sessions, tokens) +│ │ │ │ ├── battle.controller.ts # POST/GET /battles +│ │ │ │ ├── battle.service.ts # Battle session logic +│ │ │ │ ├── battle.gateway.ts # WebSocket battle updates +│ │ │ │ ├── battle.module.ts +│ │ │ │ ├── battle-maps.controller.ts # Battle map CRUD +│ │ │ │ ├── battle-maps.service.ts +│ │ │ │ ├── combatants.controller.ts # NPC/monster templates +│ │ │ │ ├── combatants.service.ts +│ │ │ │ └── dto/ +│ │ │ ├── translations/ # German translations cache +│ │ │ │ ├── translations.controller.ts # GET translations +│ │ │ │ ├── translations.service.ts # Cache & Claude API calls +│ │ │ │ ├── translations.module.ts +│ │ │ │ └── dto/ +│ │ │ └── claude/ # Claude API integration +│ │ │ ├── claude.module.ts +│ │ │ └── claude.service.ts # Claude API wrapper +│ │ └── generated/ # Generated code (do not edit) +│ │ └── prisma/ # Prisma client types & enums +│ │ ├── client.ts +│ │ ├── models.ts +│ │ ├── enums.ts +│ │ └── ... +│ ├── prisma/ # Prisma ORM configuration +│ │ ├── schema.prisma # Database schema (tables, relations, enums) +│ │ ├── migrations/ # Versioned database migrations +│ │ │ ├── 20260118162916_init/ +│ │ │ ├── 20260118225853_add_equipment_detail_fields/ +│ │ │ ├── 20260119083024_add_credits_to_character/ +│ │ │ └── 20260119111209_add_item_custom_fields/ +│ │ ├── seed.ts # Base seed script (users, etc.) +│ │ └── data/ # JSON source data for seeding +│ │ ├── equipment.json # 5,482 PF2e equipment items +│ │ ├── feats.json # PF2e talents +│ │ ├── spells.json # PF2e spells +│ │ ├── actions.json # PF2e combat actions +│ │ ├── conditions.json # PF2e conditions (Pathfinder 2e ruleset) +│ │ └── ... +│ ├── src/generated/ # Generated Prisma types +│ ├── package.json +│ ├── tsconfig.json +│ ├── nest-cli.json +│ └── dist/ # Build output (generated) +│ +├── .planning/ # Phase planning documents +│ └── codebase/ # Architecture analysis (this file) +│ ├── ARCHITECTURE.md +│ ├── STRUCTURE.md +│ ├── STACK.md +│ ├── INTEGRATIONS.md +│ ├── CONVENTIONS.md +│ ├── TESTING.md +│ └── CONCERNS.md +├── docs/ # Documentation +├── .claude/ # Claude settings & instructions +│ └── settings.local.json +├── package-lock.json # Lock file (top-level) +└── CLAUDE.md # Project guidelines (Quality > Speed) +``` + +## Directory Purposes + +### Client Structure + +**`client/src/features/`** - Feature-based modules +- Each feature is a semi-independent domain (auth, characters, campaigns, battle, library) +- Contains components specific to that feature +- Exports public API via barrel exports (`index.ts`) +- Examples: AuthFeature manages login/register, CharactersFeature manages character sheet +- Pattern: Feature-based organization allows independent testing and scaling + +**`client/src/shared/`** - Cross-feature reusables +- `components/ui/` - shadcn/ui primitive components (Button, Card, Input, etc.) +- `components/layout.tsx` - App wrapper with navbar +- `components/protected-route.tsx` - Auth enforcement for routes +- `hooks/` - Custom React hooks (useCharacterSocket, etc.) +- `lib/api.ts` - HTTP client with token management +- `lib/utils.ts` - Helper functions +- `types/` - All TypeScript interfaces shared across app +- Pattern: Single source of truth for types, no duplication + +**`client/public/`** - Static files +- `data/` - JSON files (if any live here, but equipment comes from backend) +- `icons/` - SVG icons used in UI + +### Server Structure + +**`server/src/modules/`** - NestJS modules by feature +- Each module is self-contained: controller → service → gateway +- Module exports what other modules need via `exports: []` in decorator +- Examples: + - CharactersModule exports CharactersService, CharactersGateway for cross-module use + - AuthModule exports AuthService, guards for other modules to use +- Pattern: Module-based organization with explicit dependency injection + +**`server/src/common/`** - Shared utilities +- Decorators: @CurrentUser, @Public, @Roles +- Guards: Shared by all modules +- Pattern: Cross-cutting concerns in one place + +**`server/prisma/`** - Database layer +- `schema.prisma` - Single source of truth for data model +- `migrations/` - Versioned schema changes (never use `db push`) +- `seed.ts` - Initial data +- `data/` - JSON source files for seed scripts +- Pattern: Database-first approach, all data in DB not JSON files + +## Key File Locations + +### Entry Points + +**Backend:** +- `server/src/main.ts` - Bootstrap NestJS app, enable pipes/guards/swagger +- AppModule at `server/src/app.module.ts` - Wires all modules together + +**Frontend:** +- `client/src/main.tsx` - React root render +- Router at `client/src/App.tsx` - BrowserRouter with route protection + +### Configuration + +**Environment:** +- `server/.env` - PostgreSQL, JWT_SECRET, ANTHROPIC_API_KEY, CORS_ORIGINS +- `client/.env` - VITE_API_URL (must match server PORT) +- Example files: `server/.env.example`, `client/.env.example` + +**Build & Development:** +- Backend: + - `server/nest-cli.json` - NestJS CLI config + - `server/tsconfig.json` - TypeScript settings +- Frontend: + - `client/vite.config.ts` - Vite bundler (alias paths with `@`) + - `client/tsconfig.json` - TypeScript settings (strict mode) + - `client/tailwind.config.js` - Tailwind v4 customization + - `client/postcss.config.js` - CSS processing + +### Core Logic + +**Character System:** +- Service: `server/src/modules/characters/characters.service.ts` - HP, conditions, items +- Gateway: `server/src/modules/characters/characters.gateway.ts` - Real-time sync +- Import: `server/src/modules/characters/pathbuilder-import.service.ts` - Pathbuilder parsing +- Alchemy: `server/src/modules/characters/alchemy.service.ts` - Alchemy logic +- UI: `client/src/features/characters/components/character-sheet-page.tsx` - Main page + +**Battle System:** +- Service: `server/src/modules/battle/battle.service.ts` - Sessions & tokens +- Gateway: `server/src/modules/battle/battle.gateway.ts` - Real-time sync +- Canvas: `client/src/features/battle/components/battle-canvas.tsx` - Map & tokens + +**Equipment Database:** +- Service: `server/src/modules/equipment/equipment.service.ts` - Search & filtering +- Controller: `server/src/modules/equipment/equipment.controller.ts` - Endpoints +- UI: `client/src/features/characters/components/add-item-modal.tsx` - Search UI +- Data: `server/prisma/data/equipment.json` - 5,482 items (seeded to DB) + +**Authentication:** +- Service: `server/src/modules/auth/auth.service.ts` - Login/register/JWT +- Guard: `server/src/modules/auth/guards/jwt-auth.guard.ts` - Applied globally +- Store: `client/src/features/auth/hooks/use-auth-store.ts` - Client-side auth state +- API: `client/src/shared/lib/api.ts` - Token persistence (localStorage vs sessionStorage) + +### Testing + +**Backend:** +- Test files: `server/src/**/*.spec.ts` (not yet created - opportunity) +- Config: `server/jest.config.js` (if exists) + +**Frontend:** +- Test files: `client/src/**/*.spec.tsx` (not yet created - opportunity) +- Config: `client/vitest.config.ts` (if exists) or `client/jest.config.js` + +## Naming Conventions + +### Files + +**Component files (React):** +- Pattern: kebab-case `.tsx` +- Example: `character-sheet-page.tsx`, `add-item-modal.tsx`, `hp-control.tsx` +- Convention: Lowercase with hyphens, `.tsx` for JSX + +**Service files (NestJS):** +- Pattern: kebab-case `.ts` +- Example: `characters.service.ts`, `pathbuilder-import.service.ts`, `alchemy.service.ts` +- Convention: Feature name + responsibility (service/controller/gateway) + +**Guard/Strategy files:** +- Pattern: kebab-case `.ts` +- Example: `jwt-auth.guard.ts`, `jwt.strategy.ts` + +**DTO files:** +- Pattern: kebab-case `.ts` inside `dto/` folder +- Example: `login.dto.ts`, `register.dto.ts` + +**Module files:** +- Pattern: Feature name + `.module.ts` +- Example: `auth.module.ts`, `characters.module.ts` + +### Directories + +**Feature directories:** +- Pattern: kebab-case (lowercase with hyphens) +- Example: `auth/`, `campaigns/`, `characters/`, `battle/`, `library/` +- Structure: Each feature has `components/`, `hooks/`, `utils/`, `index.ts` + +**Shared directories:** +- Pattern: Descriptive plural or functional names +- Examples: `components/`, `hooks/`, `lib/`, `types/`, `decorators/`, `strategies/` + +**Module sub-directories:** +- Pattern: `guards/`, `strategies/`, `dto/` (lowercase) +- Convention: Consistent across all modules + +## Where to Add New Code + +### New Feature + +**Backend:** +1. Create `server/src/modules/[feature]/` directory +2. Create files: + - `[feature].module.ts` - NestJS module, imports dependencies + - `[feature].controller.ts` - HTTP endpoints + - `[feature].service.ts` - Business logic + - `[feature].gateway.ts` (optional) - WebSocket if real-time needed + - `dto/` folder with DTOs +3. Import module in `server/src/app.module.ts` + +**Frontend:** +1. Create `client/src/features/[feature]/` directory +2. Create sub-folders: + - `components/` - React components + - `hooks/` - Custom hooks (if state needed) + - `index.ts` - Barrel export +3. Add routes in `client/src/App.tsx` + +**Database (if adding entities):** +1. Add model to `server/prisma/schema.prisma` +2. Run: `cd server && npm run db:migrate:dev` +3. Name migration descriptively: "add_new_feature_table" +4. Seeds automatically regenerate Prisma client + +### New Component/Module + +**React Component:** +- Location: `client/src/features/[feature]/components/[name].tsx` +- Pattern: Named export + default export +- Import shared types from `client/src/shared/types/index.ts` +- Use API client from `client/src/shared/lib/api.ts` + +**NestJS Service:** +- Location: `server/src/modules/[feature]/[name].service.ts` +- Pattern: @Injectable() class with public methods +- Inject PrismaService via constructor +- Keep business logic in service, HTTP concern in controller + +**WebSocket Gateway:** +- Location: `server/src/modules/[feature]/[name].gateway.ts` +- Pattern: @WebSocketGateway() @Injectable() class +- Authenticate in handleConnection() +- Emit broadcasts to rooms via socket.server.to(room).emit() + +### Utilities + +**Shared Frontend Helpers:** +- Location: `client/src/shared/lib/utils.ts` +- Pattern: Exported functions (no classes) +- Use for date formatting, type checks, calculations + +**Shared Backend Helpers:** +- Location: `server/src/common/` (create subdirectory if needed) +- Pattern: Injectable services or utility functions +- Example: CommonService for shared operations + +## Special Directories + +**`server/prisma/migrations/`** +- Purpose: Version control for database schema +- Generated: Auto-created by `npm run db:migrate:dev` +- Committed: YES (must be in git for reproducible deployments) +- Manual edits: Rarely needed, but can fix SQL if migration is broken +- Pattern: Timestamp-prefixed folders with `.sql` files inside + +**`server/src/generated/`** +- Purpose: Prisma-generated types and client +- Generated: Auto-created by `npm run db:generate` +- Committed: NO (regenerated on install) +- Manual edits: DO NOT EDIT — regenerate instead + +**`client/public/data/`** +- Purpose: Static JSON files (currently unused, equipment in DB) +- Generated: NO (manually added if needed) +- Committed: YES +- Pattern: Only for truly static reference data + +**`server/dist/` and `client/dist/`** +- Purpose: Compiled/bundled output +- Generated: YES (`npm run build`) +- Committed: NO (.gitignore) +- Pattern: Build artifacts, environment-specific + +**`server/node_modules/` and `client/node_modules/`** +- Purpose: Installed dependencies +- Generated: YES (`npm install`) +- Committed: NO (.gitignore) +- Pattern: Use package-lock.json for reproducibility + +--- + +*Structure analysis: 2026-04-27* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 0000000..9c09143 --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -0,0 +1,340 @@ +# Testing Patterns + +**Analysis Date:** 2026-04-27 + +## Test Framework Status + +**Current State:** **Minimal test coverage - testing infrastructure in place but not actively used** + +### Jest (Server) + +**Runner:** +- Jest 30.x +- Config: Inline in `server/package.json` (lines 88-104) + +**Run Commands:** +```bash +cd server +npm test # Run all unit tests (seeks *.spec.ts files) +npm run test:watch # Watch mode for development +npm run test:cov # Generate coverage report +npm run test:debug # Debug mode with breakpoints +npm run test:e2e # Run end-to-end tests (config: test/jest-e2e.json) +``` + +**Configuration Details:** +```json +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { "^.+\\.(t|j)s$": "ts-jest" }, + "collectCoverageFrom": ["**/*.(t|j)s"], + "coverageDirectory": "../coverage", + "testEnvironment": "node" +} +``` + +**Testing Libraries:** +- Jest 30.x (test runner) +- ts-jest (TypeScript support) +- @nestjs/testing (NestJS module testing utilities) +- supertest (HTTP request testing) + +### Vitest (Client) + +**Status:** Not installed. No vitest config present in `client/package.json` + +**Implications:** Client has zero automated test infrastructure. Any future testing would require adding vitest and configuring it. + +## Test File Organization + +**Server:** +- Location: Unit tests alongside source in `src/`, E2E tests in `test/` +- Naming: `*.spec.ts` for unit tests, `*.e2e-spec.ts` for integration tests +- Example: No unit test files currently exist in `server/src/` + +**Current Test Suite:** +- Location: `server/test/app.e2e-spec.ts` +- Scope: Single basic integration test for app startup +- Status: Placeholder test (checks 'Hello World!' endpoint that doesn't exist) + +**Client:** +- No test files present +- No test directory structure + +## Test Structure (Server) + +**E2E Test Example** (`server/test/app.e2e-spec.ts`): +```typescript +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); +}); +``` + +**Patterns:** +- `describe()` for test suites +- `it()` for individual tests (or `test()`) +- `beforeEach()` for setup (module initialization) +- Async/await for async operations +- Chained `.expect()` assertions with supertest + +## Mocking + +### Server (Jest/NestJS) + +**Framework:** @nestjs/testing provides test module builder + +**Pattern Observed:** +```typescript +const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], // Or specific module imports +}).compile(); + +const service = moduleFixture.get(ServiceName); +``` + +**What to Mock:** +- Database (Prisma) - would use mock provider +- External APIs - would use jest.mock() +- Services with dependencies - inject via module builder + +**What NOT to Mock:** +- NestJS core decorators +- Guards/Middleware (test through integration) +- Database schema (use test database) + +### Client + +**No mocking patterns established yet** - testing infrastructure not present. + +Would require: +- Vitest for runner +- `@testing-library/react` for component testing +- Mock fetch/axios calls +- Mock WebSocket connections + +## Fixtures and Factories + +**Server:** + +**Test Data Approach:** Not fully established, but patterns available: +- Prisma seed scripts exist in `server/prisma/`: + - `seed.ts` - Basic seed + - `seed-equipment.ts` - 5,482 equipment items + - `seed-feats.ts` - Feat database +- Could be reused for test fixtures + +**Location:** Would be in test directory or integrated with jest setup + +**No factory pattern observed** - tests would need to build objects inline or create helper functions + +**Client:** + +**Not applicable** - no test infrastructure + +## Coverage + +**Requirements:** None enforced (no CI/CD pipeline checking coverage) + +**View Coverage:** +```bash +cd server +npm run test:cov +# Generates: server/coverage/ +``` + +**Current Coverage:** Unknown (no tests running) + +**Gaps:** **Entire codebase untested** +- Auth module: No unit tests (`server/src/modules/auth/`) +- Characters module: No unit tests (`server/src/modules/characters/`) +- Battle module: No unit tests (`server/src/modules/battle/`) +- WebSocket gateway: No tests +- Guards/Decorators: No tests +- Client components: No tests +- Client hooks: No tests +- State management (Zustand): No tests + +## Test Types + +### Unit Tests + +**Not currently used** + +**When implemented, pattern would be:** +- Test individual services in isolation +- Mock Prisma client +- Location: `server/src/modules/[module]/[module].service.spec.ts` +- Example test structure (not yet used): +```typescript +describe('AuthService', () => { + let service: AuthService; + let prisma: PrismaService; + + beforeEach(async () => { + const module = await Test.createTestingModule({ + providers: [AuthService, { provide: PrismaService, useValue: mockPrisma }], + }).compile(); + + service = module.get(AuthService); + }); + + it('should register user', async () => { + // Test implementation + }); +}); +``` + +### Integration Tests + +**Single example:** `server/test/app.e2e-spec.ts` + +**Scope:** +- Tests full HTTP request/response cycle +- Uses real app instance +- Uses Test.createTestingModule() to create app + +**Pattern:** +```typescript +return request(app.getHttpServer()) + .post('/auth/login') + .send({ identifier: 'user', password: 'pass' }) + .expect(200) + .expect((res) => { + expect(res.body.token).toBeDefined(); + }); +``` + +**Would need:** Database test fixtures, cleanup between tests + +### E2E Tests + +**Config:** `server/test/jest-e2e.json` + +**Difference from Integration:** +- Run against deployed/separate server +- Test user workflows end-to-end +- Full database setup/teardown + +**Currently:** Not implemented beyond placeholder + +## Common Patterns + +### Async Testing + +**Server (Jest):** +```typescript +it('should load data', async () => { + const result = await service.loadData(); + expect(result).toBeDefined(); +}); +``` + +- async/await preferred +- Supertest chainable: `request().post().send().expect()` + +**Client (not yet implemented):** +Would use: +```typescript +const { result } = await renderHook(() => useCharacterSocket()); +await waitFor(() => expect(result.current.socket).toBeDefined()); +``` + +### Error Testing + +**Not yet implemented, but pattern would be:** + +**Server:** +```typescript +it('should throw NotFoundException', async () => { + await expect(service.getCharacter('invalid-id')) + .rejects + .toThrow(NotFoundException); +}); +``` + +**Actual exception handling verified through:** +- Controllers catch exceptions +- Guards handle auth failures +- Integration tests verify HTTP status codes + +## Critical Test Gaps + +**High Priority (Authentication):** +- AuthService.register() - conflicts, hashing, JWT generation +- AuthService.login() - invalid credentials, token generation +- JWT guard - token validation, public routes +- File: `server/src/modules/auth/auth.service.ts` (lines 20-104) + +**High Priority (Character Management):** +- CharactersService access control (checkCampaignAccess, checkCharacterAccess) +- HP updates and state sync via WebSocket +- Condition application (dying, wounded, doomed mechanics) +- Item management (equip, invest, notes) +- File: `server/src/modules/characters/characters.service.ts` + +**High Priority (Real-time Sync):** +- CharactersGateway connection authentication +- WebSocket message broadcasting +- Concurrent client updates +- File: `server/src/modules/characters/characters.gateway.ts` + +**Medium Priority (Client UI):** +- HpControl component (damage, heal, direct modes) +- AddConditionModal (search, filtering, validation) +- Character import from Pathbuilder JSON +- File: `client/src/features/characters/components/` + +**Medium Priority (Validation):** +- DTO validation (CreateCharacterDto, LoginDto) +- Prisma query results +- File: `server/src/modules/*/dto/` + +## Recommendations for Implementation + +**Phase 1 (Auth):** +1. Write AuthService unit tests with mocked Prisma +2. Write JwtAuthGuard tests +3. Write integration tests for login/register endpoints + +**Phase 2 (Character CRUD):** +1. Test CharactersService access control +2. Test HP update logic +3. Test condition application + +**Phase 3 (WebSocket):** +1. Mock socket.io for gateway tests +2. Test connection/disconnect +3. Test broadcast patterns + +**Phase 4 (Client):** +1. Setup vitest +2. Test HpControl component +3. Test modal components +4. Test Zustand store + +--- + +*Testing analysis: 2026-04-27*