docs: map existing codebase

This commit is contained in:
2026-04-27 09:13:11 +02:00
parent 15dc0d289a
commit 3e91d5713d
7 changed files with 2237 additions and 0 deletions

View File

@@ -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*

View File

@@ -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*

View File

@@ -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<Character>`
- Component callbacks: callbacks are async promises (`onHpChange: (newHp: number) => Promise<void>`)
- 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<AuthState>()`
- 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*

View File

@@ -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*

164
.planning/codebase/STACK.md Normal file
View File

@@ -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*

View File

@@ -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*

View File

@@ -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>(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>(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*