Rehydration functions (reconstructing typed domain objects from untyped JSON) lived in persistence adapters, duplicating domain validation. Adding a field required updating both the domain type and a separate adapter function — the adapter was missed for `level`, silently dropping it on reload. Now adding a field only requires updating the domain type and its co-located rehydration function. - Add `rehydratePlayerCharacter` and `rehydrateCombatant` to domain - Persistence adapters delegate to domain instead of reimplementing - Add `tempHp` validation (was silently dropped during rehydration) - Tighten initiative validation to integer-only - Exhaustive domain tests (53 cases); adapter tests slimmed to round-trip - Remove stale `jsinspect-plus` Knip ignoreDependencies entry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
34 lines
868 B
TypeScript
34 lines
868 B
TypeScript
import type { PlayerCharacter } from "@initiative/domain";
|
|
import { rehydratePlayerCharacter } from "@initiative/domain";
|
|
|
|
const STORAGE_KEY = "initiative:player-characters";
|
|
|
|
export function savePlayerCharacters(characters: PlayerCharacter[]): void {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(characters));
|
|
} catch {
|
|
// Silently swallow errors (quota exceeded, storage unavailable)
|
|
}
|
|
}
|
|
|
|
export function loadPlayerCharacters(): PlayerCharacter[] {
|
|
try {
|
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
if (raw === null) return [];
|
|
|
|
const parsed: unknown = JSON.parse(raw);
|
|
if (!Array.isArray(parsed)) return [];
|
|
|
|
const characters: PlayerCharacter[] = [];
|
|
for (const item of parsed) {
|
|
const pc = rehydratePlayerCharacter(item);
|
|
if (pc !== null) {
|
|
characters.push(pc);
|
|
}
|
|
}
|
|
return characters;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|