feat(events): event log read — getEvents + limit parsing
lib/game/events.ts: the latest N events of the save, newest first (ORDER BY id DESC), with parseEventLimit for the optional ?limit= param (default 50, cap 200, 400 otherwise). Unit tests cover default/accept/reject and newest-first ordering + slicing.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import { desc } from 'drizzle-orm'
|
||||
import * as schema from '@/lib/db/schema'
|
||||
import type { GameDb } from '@/lib/db/client'
|
||||
|
||||
export interface EventDto {
|
||||
id: number
|
||||
turn: number
|
||||
type: string
|
||||
message: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export const DEFAULT_LIMIT = 50
|
||||
export const MAX_LIMIT = 200
|
||||
|
||||
export class EventError extends Error {
|
||||
constructor(
|
||||
public readonly status: number,
|
||||
message: string,
|
||||
) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the optional ?limit= query param. Absent → default; anything that is not an integer in
|
||||
// [1, MAX_LIMIT] is a 400 (the route maps EventError to its status).
|
||||
export function parseEventLimit(raw: string | null): number {
|
||||
if (raw === null) return DEFAULT_LIMIT
|
||||
const limit = Number(raw)
|
||||
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
||||
throw new EventError(400, `limit must be an integer between 1 and ${MAX_LIMIT}`)
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
// The latest N events of the save, newest first. Assumes a save exists (the route calls ensureSave).
|
||||
export async function getEvents(db: GameDb, limit: number): Promise<EventDto[]> {
|
||||
const rows = await db.db.select().from(schema.events).orderBy(desc(schema.events.id)).limit(limit)
|
||||
return rows.map((e) => ({ id: e.id, turn: e.turn, type: e.type, message: e.message, createdAt: e.createdAt }))
|
||||
}
|
||||
Reference in New Issue
Block a user