mundane.api.app

The Litestar application: HTTP in, engine actions out, state back as JSON.

The API is a thin translator. It never mutates GameState directly — it parses requests into engine actions and calls Game.submit, then maps the engine’s IllegalAction onto HTTP 422. All game legality lives in the engine. Loading a game’s cards (allowlist, fetch, schema-validate, snapshot) lives in mundane.api.set_loader; the engine receives only the resolved pool.

The game store is an in-memory dict, supplied via dependency injection. It is volatile (lost on restart); see the README. Keeping it behind the small GameStore interface (create / get / save) makes swapping it for Redis or SQLite a localised change.

Attributes

app

Classes

GameStore

In-memory game store. Volatile: its contents are lost when the process restarts.

Functions

_unprocessable_handler(→ litestar.Response[dict[str, str]])

Map a rejected move or bad set input onto HTTP 422; the stored game is left unchanged.

_bad_gateway_handler(→ litestar.Response[dict[str, str]])

Map an upstream set-fetch failure (network/timeout/oversize) onto HTTP 502.

_parse_set_urls(→ collections.abc.Sequence[str] | None)

Pull set_urls out of the request body, rejecting anything that isn't a list of strings.

create_game(→ dict[str, object])

Create a new game from set_urls (default: the core set); return its id and initial state.

read_game(→ dict[str, object])

Return the current state of a game.

submit_action(→ dict[str, object])

Submit a move. An illegal move raises IllegalAction (-> 422) and changes nothing.

export_game(→ litestar.Response[dict[str, object]])

Return the game's log, final state, and card snapshot as a downloadable JSON attachment.

create_app(→ litestar.Litestar)

Build the Litestar app. Pass a store to share/inspect it (tests do); else a fresh one.

Module Contents

class mundane.api.app.GameStore(*, fetch: mundane.api.set_loader.Fetcher = default_fetch)[source]

In-memory game store. Volatile: its contents are lost when the process restarts.

Deliberately an opaque service (not a dataclass), so the framework treats it as an injected dependency rather than request data to introspect. Swapping in Redis or SQLite later means reimplementing just create / get / save behind this same interface. fetch is injectable so tests can resolve set URLs from a local fixture instead of the network.

games: dict[str, mundane.engine.game.Game][source]
_fetch[source]
create(set_urls: collections.abc.Sequence[str] | None = None) tuple[str, mundane.engine.game.Game][source]

Resolve set_urls (default: the core set), then create and store a new game.

Loading happens before the store is touched, so any loader error leaves it unchanged.

get(game_id: str) mundane.engine.game.Game[source]

Return the stored game, or raise NotFoundException (-> 404) if there is none.

save(game_id: str, game: mundane.engine.game.Game) None[source]

Persist the game. A no-op for the in-memory store (games already mutate in place).

mundane.api.app._unprocessable_handler(_request: litestar.Request[Any, Any, Any], exc: Exception) litestar.Response[dict[str, str]][source]

Map a rejected move or bad set input onto HTTP 422; the stored game is left unchanged.

mundane.api.app._bad_gateway_handler(_request: litestar.Request[Any, Any, Any], exc: Exception) litestar.Response[dict[str, str]][source]

Map an upstream set-fetch failure (network/timeout/oversize) onto HTTP 502.

mundane.api.app._parse_set_urls(data: dict[str, object] | None) collections.abc.Sequence[str] | None[source]

Pull set_urls out of the request body, rejecting anything that isn’t a list of strings.

mundane.api.app.create_game(store: GameStore, data: dict[str, object] | None = None) dict[str, object][source]

Create a new game from set_urls (default: the core set); return its id and initial state.

mundane.api.app.read_game(game_id: str, store: GameStore) dict[str, object][source]

Return the current state of a game.

mundane.api.app.submit_action(game_id: str, data: dict[str, object], store: GameStore) dict[str, object][source]

Submit a move. An illegal move raises IllegalAction (-> 422) and changes nothing.

mundane.api.app.export_game(game_id: str, store: GameStore) litestar.Response[dict[str, object]][source]

Return the game’s log, final state, and card snapshot as a downloadable JSON attachment.

The snapshot (resolved cards + content hash) makes the download self-contained: it replays without reaching the cards repo. The Content-Disposition attachment header is what the “Download game log” button on the game-over screen points at.

mundane.api.app.create_app(store: GameStore | None = None) litestar.Litestar[source]

Build the Litestar app. Pass a store to share/inspect it (tests do); else a fresh one.

mundane.api.app.app[source]