Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cec851d
remove unnecessary checks in middleware
tudddorrr Feb 27, 2026
feb34e6
Merge pull request #868 from TaloDev/remove-unnecessary-middleware-ch…
tudddorrr Feb 27, 2026
3f81307
vite 7
tudddorrr Feb 27, 2026
0dc0fb6
preserve ignores for coverage
tudddorrr Feb 27, 2026
7b89393
Merge pull request #869 from TaloDev/preserve-ignores
tudddorrr Feb 27, 2026
f9751b5
longer last attempt for steamworks, return better error code
tudddorrr Feb 28, 2026
a26dc43
Merge pull request #870 from TaloDev/improve-steam-client
tudddorrr Feb 28, 2026
2c1b32c
fix player session race conditions
tudddorrr Feb 28, 2026
21d756a
Merge pull request #871 from TaloDev/fix-session-race-conditions
tudddorrr Feb 28, 2026
a031f30
Update dependency @types/lodash to v4.17.24 (#872)
renovate[bot] Mar 1, 2026
15ec963
Update dependency @types/nodemailer to v6.4.23 (#873)
renovate[bot] Mar 1, 2026
a4238d6
archiving feedback
tudddorrr Mar 1, 2026
e484a89
Merge pull request #874 from TaloDev/archived-feedback
tudddorrr Mar 1, 2026
09c30cc
add feedback category resetting
tudddorrr Mar 1, 2026
4f76358
Merge pull request #875 from TaloDev/reset-feedback-category
tudddorrr Mar 1, 2026
508a605
chore: upgrade dinero.js to v2.0.0 stable
sarahdayan Mar 3, 2026
e7f5dc4
Merge pull request #876 from sarahdayan/chore/upgrade-dinero-v2
tudddorrr Mar 4, 2026
baba732
formatting
tudddorrr Mar 5, 2026
5c1c9c5
use nodenext for module + moduleResolution
tudddorrr Mar 5, 2026
a285d06
correctly calculate global value after stat reset
tudddorrr Mar 5, 2026
aa97c86
Merge pull request #878 from TaloDev/reset-stat-calculate-global-value
tudddorrr Mar 5, 2026
814d88f
retry stat upsert on lock wait timeout
tudddorrr Mar 5, 2026
6367cea
0.109.0
tudddorrr Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 6 additions & 56 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

Talo is a self-hostable game development services platform providing leaderboards, player authentication, peer-to-peer multiplayer, event tracking, and more. The backend is built with Koa (Node.js web framework) using `koa-tree-router` for routing.
Talo is a self-hostable game development services platform providing leaderboards, player authentication, peer-to-peer multiplayer, event tracking and more.

### Testing

```bash
npm test # Run all tests with Vitest
npm test path/to/file # Run specific test file
npm test -- --coverage # Run with coverage report
```

Tests run against fresh Docker containers. Environment variables from `.env` are combined with `envs/.env.test`.

### Building & Linting

```bash
npm run lint -- --check # Run Oxlint + type-checker
npm run lint -- --type-check # Run Oxlint + tsc
```

### Database Migrations
Expand Down Expand Up @@ -70,35 +69,6 @@ Then route-specific middleware:

Finally, route handlers execute.

### Entity Layer (Models)

MikroORM entities with TypeScript decorators:

```typescript
import { Entity, Property, ManyToOne } from '@mikro-orm/core'

@Entity()
export default class Player {
@PrimaryKey()
id: number

@Property()
createdAt: Date = new Date()

@ManyToOne(() => Game, { lazy: true })
game: Game

@Property()
props: PlayerProp[] = []
}
```

### Database Architecture

- **MySQL** (MikroORM): Core relational data (users, games, players, leaderboards)
- **Redis**: Caching, job queue storage (BullMQ), session data
- **ClickHouse**: Analytics time-series data (events, metrics)

All handlers receive `ctx.em` (EntityManager) for queries. Migrations run automatically on startup (except in test mode).

### MikroORM Identity Map & Request Context
Expand All @@ -124,16 +94,6 @@ const alias = await ctx.em.repo(PlayerAlias).findOne({
// No need to explicitly load it via `fields: ['player.game.id']`
```

The request context is set up via middleware using Node's `AsyncLocalStorage`, ensuring parallel requests don't interfere with each other.

### Background Jobs & Scheduling

BullMQ for async jobs, configured in `src/config/global-queues.ts`. Scheduled tasks defined in `src/config/scheduled-tasks.ts`:

- Archive leaderboard entries
- Delete inactive players
- Cleanup jobs

### WebSocket Layer

Real-time communication via custom WebSocket implementation in `src/socket/`:
Expand Down Expand Up @@ -178,9 +138,10 @@ Use the `/new-route` skill for step-by-step guidance on creating routes.
### Adding a New Entity

1. Create entity in `src/entities/my-entity.ts` with decorators
2. Run `npm run migration:create` to generate migration
3. Rename and register migration in `src/migrations/index.ts`
4. MikroORM will auto-migrate on next startup
2. Register it in `src/entities/index.ts`
3. Run `npm run migration:create` to generate migration
4. Rename and register migration in `src/migrations/index.ts`
5. MikroORM will auto-migrate on next startup

### Error Handling

Expand All @@ -190,8 +151,6 @@ All errors caught by `src/middleware/error-middleware.ts`:
- Sentry integration for production
- OpenTelemetry tracing context

Throw errors with status codes: `ctx.throw(400, 'Invalid request')`

Use `return ctx.throw()` pattern when you need TypeScript to narrow types after the throw:

```typescript
Expand All @@ -207,19 +166,10 @@ if (!player) {
- **Authentication**: Handled by middleware (JWT validation, API key extraction)
- **Authorization**: Handled by middleware (user type gates, API scopes)

### Working with Props

Props are flexible key-value pairs on entities (Player, Game, LeaderboardEntry):

- Validated size limits (prevent abuse)
- Stored as JSON in database
- Access via entity's `props` array

## Important Conventions

- Entity names are singular (Player, not Players)
- Router functions are named `[feature]Router` or `[feature]APIRouter`
- Test files end with `.test.ts`
- Migration files: `[Timestamp][PascalCaseDescription].ts`
- Use lazy loading for entity relationships to avoid circular dependencies
- API endpoints require scope checks via `requireScopes()` middleware
Expand Down
Loading