feat(01-02): shared types module for leveling lib

Task 1 of Plan 01-02. Types-only file (no runtime behavior, no test).

- Proficiency union (mirrors Prisma enum)
- PROFICIENCY_BASE_BONUS lookup (untrained 0, trained 2, ..., legendary 8)
- EvalResult discriminated union for prereq evaluation
- StepKind union for wizard step ordering (D-10)
- CharacterContext interface for prereq + recompute inputs
- DerivedStats interface — does NOT include hpCurrent (Pitfall #9)
- ClassProgressionRow interface (read-only)
- WizardChoices interface
This commit is contained in:
2026-04-27 14:05:04 +02:00
parent 7e40449e68
commit 4d2cb5e529

View File

@@ -0,0 +1,88 @@
/**
* Shared types for the Level-Up pure-function library.
* No runtime dependencies — types only.
*/
import type { AbilityAbbreviation } from './apply-attribute-boost';
export type { AbilityAbbreviation };
/** PF2e proficiency ranks (mirrors Prisma `Proficiency` enum). */
export type Proficiency = 'UNTRAINED' | 'TRAINED' | 'EXPERT' | 'MASTER' | 'LEGENDARY';
/** Numeric proficiency bonus per rank, for use in proficiencyBonus(rank, level) calculation. */
export const PROFICIENCY_BASE_BONUS: Record<Proficiency, number> = {
UNTRAINED: 0,
TRAINED: 2,
EXPERT: 4,
MASTER: 6,
LEGENDARY: 8,
};
/** Discriminated union for prereq evaluation result. */
export type EvalResult =
| { ok: true }
| { ok: false; reason: string }
| { unknown: true; raw: string };
/** Ordered union of wizard step kinds (UI-SPEC + RESEARCH §Pattern 1). */
export type StepKind =
| 'class-features'
| 'class-feature-choice'
| 'boost'
| 'skill-increase'
| 'feat-class'
| 'feat-skill'
| 'feat-general'
| 'feat-ancestry'
| 'feat-archetype'
| 'spellcaster'
| 'review';
/** Snapshot a character's mechanical state for prereq evaluation and recompute. */
export interface CharacterContext {
level: number;
className: string;
ancestryName: string;
heritageName?: string;
abilities: Record<AbilityAbbreviation, number>;
skills: Record<string, Proficiency>;
feats: Set<string>;
}
/** Output of recomputeDerivedStats — never includes hpCurrent (Pitfall #9). */
export interface DerivedStats {
level: number;
hpMax: number;
ac: number;
classDc: number;
perception: number;
fortitude: number;
reflex: number;
will: number;
}
/** ClassProgression row shape — read-only input to recompute pipeline. */
export interface ClassProgressionRow {
className: string;
level: number;
grants: string[];
proficiencyChanges: Partial<Record<'fortitude' | 'reflex' | 'will' | 'perception' | 'classDc' | 'ac', Proficiency>>;
spellSlotIncrement?: { tradition: string; spellLevel: number; count: number } | null;
cantripIncrement?: number | null;
repertoireIncrement?: number | null;
choiceType?: string | null;
choiceOptionsRef?: string | null;
}
/** Wizard choices subset — what the user picked across the wizard. */
export interface WizardChoices {
boostTargets?: AbilityAbbreviation[];
skillIncrease?: { skillName: string; toRank: Proficiency };
featClassId?: string;
featSkillId?: string;
featGeneralId?: string;
featAncestryId?: string;
featArchetypeId?: string;
classFeatureChoices?: Record<string, string>;
spellcasterRepertoirePicks?: string[];
}