A report dumps everything needed to understand a game that went wrong:
the full state (decks card by card, shop row and tier deck order,
discards, pending choices, banked Mana/Trumpets/apples), the round's
battles, and the whole event log. Available as JSON, which replays, or
text, which reads — and the text ends with a paste-ready repro test.
Battles now record the randomness they consume alongside what they
started from, so a result can be replayed long after the round cleared
those banks: same lineups, same dice, same events, down to the log text.
Results predating the recording say so rather than quietly re-rolling.
Three ways in: the 🐛 panel's download/copy buttons, GET
/api/debug/report (DEBUG-only, since a report holds both players' hands
and the shop deck order), and `mise run report`, which reads the
database directly so a live game can be dumped without DEBUG.
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import type { Card, Session } from './types'
|
|
|
|
const SESSION_KEY = 'sapbg-session'
|
|
|
|
async function post(path: string, body: unknown): Promise<Session> {
|
|
const res = await fetch(path, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body),
|
|
})
|
|
const data = await res.json()
|
|
if (!res.ok) throw new Error(data.error ?? 'request failed')
|
|
return data as Session
|
|
}
|
|
|
|
export type BotDifficulty = 'easy' | 'medium' | 'hard'
|
|
|
|
export function createGame(name: string): Promise<Session> {
|
|
return post('/api/games', { name })
|
|
}
|
|
|
|
export function joinGame(code: string, name: string): Promise<Session> {
|
|
return post('/api/join', { code, name })
|
|
}
|
|
|
|
export async function fetchCatalog(packs?: string[]): Promise<Card[]> {
|
|
const query = (packs ?? [])
|
|
.map((p) => `pack=${encodeURIComponent(p)}`)
|
|
.join('&')
|
|
const res = await fetch(query ? `/api/catalog?${query}` : '/api/catalog')
|
|
if (!res.ok) throw new Error('failed to load catalog')
|
|
return (await res.json()) as Card[]
|
|
}
|
|
|
|
// debugReportURL builds the link to a game's debug report — the full state,
|
|
// battles and event log, enough to replay the situation in a test. Served only
|
|
// when the server runs with DEBUG on. `text` renders it for reading; otherwise
|
|
// it downloads as JSON, which is the form a test can replay.
|
|
export function debugReportURL(session: Session, format: 'json' | 'text', note?: string): string {
|
|
const params = new URLSearchParams({
|
|
game: session.gameId,
|
|
player: session.playerId,
|
|
token: session.token,
|
|
format,
|
|
})
|
|
if (note) params.set('note', note)
|
|
return `/api/debug/report?${params}`
|
|
}
|
|
|
|
export async function fetchDebugReportText(session: Session, note?: string): Promise<string> {
|
|
const res = await fetch(debugReportURL(session, 'text', note))
|
|
if (!res.ok) throw new Error('failed to fetch the debug report')
|
|
return res.text()
|
|
}
|
|
|
|
export function loadSession(): Session | null {
|
|
try {
|
|
const raw = localStorage.getItem(SESSION_KEY)
|
|
return raw ? (JSON.parse(raw) as Session) : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function saveSession(s: Session) {
|
|
localStorage.setItem(SESSION_KEY, JSON.stringify(s))
|
|
}
|
|
|
|
export function clearSession() {
|
|
localStorage.removeItem(SESSION_KEY)
|
|
}
|