import type { DomainEvent } from "./events.js"; import type { CombatantId, DomainError, Encounter } from "./types.js"; export interface AddCombatantSuccess { readonly encounter: Encounter; readonly events: DomainEvent[]; } /** * Pure function that adds a combatant to the end of an encounter's list. * * FR-001: Accepts an Encounter, CombatantId, and name; returns next state + events. * FR-002: Appends new combatant to end of combatants list. * FR-004: Rejects empty/whitespace-only names with DomainError. * FR-005: Does not alter activeIndex or roundNumber. * FR-006: Events returned as values, not dispatched via side effects. */ export function addCombatant( encounter: Encounter, id: CombatantId, name: string, ): AddCombatantSuccess | DomainError { const trimmed = name.trim(); if (trimmed === "") { return { kind: "domain-error", code: "invalid-name", message: "Combatant name must not be empty", }; } const position = encounter.combatants.length; return { encounter: { combatants: [...encounter.combatants, { id, name: trimmed }], activeIndex: encounter.activeIndex, roundNumber: encounter.roundNumber, }, events: [ { type: "CombatantAdded", combatantId: id, name: trimmed, position, }, ], }; }