- Games record start and move times, spectator peak and distinct count
- Games end as completed, left (the leaver loses), timeout (5 minutes
offline on your turn while the opponent is online) or inactive (both
offline for 30 minutes); everyone gets on_game_end
- Connection info carries offlineSince for the countdown
- A new game is rejected while the user has one in progress
- Live games are written to Valkey after every change and restored on
startup; finished games are saved to Postgres with public ids, and
saves that fail are kept and retried every 30 seconds
- Clients get a trimmed game without spectator ids
- Startup waits for Valkey, restores games, then accepts connections;
migrations are retried while Postgres is unreachable
- Valkey client that fails commands at once while Valkey is down instead
of queueing them, and logs connection errors once
- ValkeyLiveGameStore: live game snapshots and finished games waiting to
be saved, loaded with SCAN on startup
- ValkeyCache: JSON values with a TTL; errors count as cache misses
- Integration tests against a real Valkey, including an unreachable one
- Connection pool and a small migration runner (schema_migrations, one
transaction per migration, advisory lock)
- games and game_players tables with indexes for a user's games and for
popular games
- PostgresGameHistoryRepository: idempotent save, get, recent games of a
user, popular games
- Integration tests against a real Postgres via testcontainers; a custom
Jest environment gives tests Node's web globals, which Jest 27 leaves out
- GameRecord carries the number of distinct spectators
- Spectators are removed from games when they disconnect; the lookup
iterated an iterator with for...in and never found anything
- Finished and left games are removed from memory, not only from the
player mappings
- The connection info is sent once when a game starts
- PlayerSummary, MoveRecord, live and finished game summaries, GameRecord,
timeout constants, startedAt/moveTimes on Game and offlineSince on
UserConnectionInfo
- Channels for live game previews and for rejecting a new game while one
is in progress
- getInitialBoard, getBoardAfterMove, describeMove and createGameAtMove,
with tests against games played with the engine
- English and Turkish strings for the new screens
- postgres replaces keycloak-db and holds a keycloak and a mancala database,
each with its own user, created by apps/postgres/init
- valkey with an append-only file for live game state and caches
- backend gets DATABASE_URL and VALKEY_URL