- Type response bodies in gameController.test.ts to fix no-unsafe-member-access
- Replace async methods with Promise.resolve() in InMemoryGameSessionStore
and InMemoryLobbyGameStore to satisfy require-await rule
- Add argsIgnorePattern and varsIgnorePattern to eslint config so
underscore-prefixed params are globally ignored
- Fix no-misused-promises in ws/index.ts, lobbyHandlers, gameHandlers,
__root.tsx, login.tsx and play.tsx by using void + .catch()
- Fix no-floating-promises on navigate calls in login.tsx
- Move API_URL outside Play component to fix useCallback dependency warning
- Type fetch response bodies in play.tsx to fix no-unsafe-assignment
- Add only-throw-error: off for route files (TanStack Router throw redirect)
- Remove unused WebSocket import from express.d.ts
- Fix unsafe return in connections.ts by typing empty Map constructor
- Exclude scripts/ folder from eslint
- Add targeted override for better-auth auth-client.ts (upstream typing issue)
- POST /api/v1/lobbies creates a lobby with a Crockford-Base32
6-char code, retrying on unique violation up to 5 times
- POST /api/v1/lobbies/:code/join validates lobby state then
calls the model's atomic addPlayer, idempotent for repeat
joins from the same user
- Routes require authentication via requireAuth
- Update all package names from @glossa/* to @lila/*
- Update all imports, container names, volume names
- Update documentation references
- Recreate database with new credentials
- Unit tests for createGameSession and evaluateAnswer (14 tests)
- Endpoint tests for POST /game/start and /game/answer via supertest (8 tests)
- Mock @glossa/db — no real database dependency
Complete the game answer flow:
- Add evaluateAnswer service function: looks up the session in the
GameSessionStore, compares the submitted optionId against the stored
correct answer, returns an AnswerResult.
- Add submitAnswer controller with safeParse validation and error
handling (session/question not found → 404).
- Add POST /api/v1/game/answer route.
- Fix createGameSession: was missing the answerKey tracking and the
gameSessionStore.create() call, so sessions were never persisted.
The full singleplayer game loop now works end-to-end:
POST /game/start → GameSession, POST /game/answer → AnswerResult.
Extend the game flow from raw term rows to a complete GameSession
matching the shared schema:
- Add getDistractors model query: fetches N same-POS, same-difficulty,
same-target-language terms excluding a given termId. Returns bare
strings since distractors only need their display text.
- Fix getGameTerms select clause to use the neutral field names
(sourceText, targetText, sourceGloss) that the type already declared.
- Rename gameService entry point to createGameSession; signature now
takes a GameRequest and returns a GameSession.
- Per question: fetch 3 distractors, combine with the correct answer,
shuffle (Fisher-Yates), assign optionIds 0-3 by post-shuffle index,
and assemble into a GameQuestion with a fresh UUID.
- Wrap the questions in a GameSession with its own UUID.
- Run per-question distractor fetches in parallel via Promise.all.
Known gap: the correct option is not yet remembered server-side, so
/game/answer cannot evaluate submissions. SessionStore lands next.
- Add GameRequestSchema and derived types to packages/shared
- Add SupportedLanguageCode, SupportedPos, DifficultyLevel type exports
- Add getGameTerms() model to packages/db with pos/language/difficulty/limit filters
- Add prepareGameQuestions() service skeleton in apps/api
- Add createGame controller with Zod safeParse validation
- Wire POST /api/v1/game/start route
- Add scripts/gametest/test-game.ts for manual end-to-end testing