refactor: complete error handling standardization (issue #101)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m26s

Prestige Store:
- Convert doPrestige() to return Result<void> with specific error codes
  (INVALID_PRESTIGE_ID, PRESTIGE_MAX_LEVEL, INSUFFICIENT_INSIGHT)
- Convert startPactRitual() to return Result<void> with specific error codes
  (GUARDIAN_NOT_DEFEATED, PACT_ALREADY_SIGNED, PACT_SLOTS_FULL,
   INSUFFICIENT_MANA, RITUAL_IN_PROGRESS)

Combat Actions:
- Add try/catch wrapper inside processCombatTick with safe fallback defaults
- Add makeDefaultCombatTickResult helper for error recovery

LocalStorage Error Handling:
- Create safe-persist.ts utility wrapping localStorage with error handling
  (corrupted JSON, quota exceeded, unexpected failures)
- Update all 8 Zustand stores to use createSafeStorage() in persist middleware

UI Updates:
- Update GuardianPactsTab to use Result pattern for ritual error messages

Tests:
- Update store-actions-combat-prestige.test.ts for Result return types
- Update store-actions.test.ts ManaStore tests for Result pattern
- Remove duplicate Prestige/Discipline sections from store-actions.test.ts
- All files under 400 line limit

601 tests pass (3 pre-existing failures in spire-utils.test.ts)
This commit is contained in:
2026-05-22 09:19:20 +02:00
parent 8a7ddaae27
commit 49f8de01ca
21 changed files with 542 additions and 547 deletions
+7 -5
View File
@@ -5,6 +5,8 @@ import type { EquipmentInstance, EquipmentCraftingProgress } from './types';
import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/crafting-recipes';
import { EQUIPMENT_TYPES } from './data/equipment';
import { generateInstanceId } from './crafting-utils';
import { ok, fail, ErrorCode } from './utils/result';
import type { Result } from './utils/result';
// ─── Equipment Crafting Validation ──────────────────────────────────────────
@@ -110,14 +112,14 @@ export function calculateCraftingTick(currentProgress: number, required: number)
export function completeEquipmentCrafting(
blueprintId: string,
recipe: CraftingRecipe
): {
): Result<{
instanceId: string;
instance: EquipmentInstance;
logMessage: string;
} {
}> {
const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId];
if (!equipType) {
throw new Error(`Invalid equipment type: ${recipe.equipmentTypeId}`);
return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`);
}
const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
@@ -133,11 +135,11 @@ export function completeEquipmentCrafting(
tags: [],
};
return {
return ok({
instanceId,
instance: newInstance,
logMessage: `🔨 Crafted ${recipe.name}!`,
};
});
}
// ─── Crafting Cancellation ──────────────────────────────────────────────────