Files
Mana-Loop/src/lib/game/utils/result.ts
T
n8n-gitea 49f8de01ca
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m26s
refactor: complete error handling standardization (issue #101)
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)
2026-05-22 09:19:20 +02:00

90 lines
2.9 KiB
TypeScript

// ─── Standardized Result Type ─────────────────────────────────────────────────
// Provides consistent error handling across the codebase.
// Use Result<T> for expected failures; reserve throw for truly unexpected errors.
/**
* Error codes for categorizing failures.
* Enables callers to programmatically handle specific error types.
*/
export const ErrorCode = {
// Mana errors
INSUFFICIENT_MANA: 'INSUFFICIENT_MANA',
ELEMENT_NOT_UNLOCKED: 'ELEMENT_NOT_UNLOCKED',
ELEMENT_MAX_CAPACITY: 'ELEMENT_MAX_CAPACITY',
INVALID_ELEMENT: 'INVALID_ELEMENT',
// Crafting errors
INVALID_BLUEPRINT: 'INVALID_BLUEPRINT',
BLUEPRINT_NOT_ACQUIRED: 'BLUEPRINT_NOT_ACQUIRED',
MISSING_MATERIALS: 'MISSING_MATERIALS',
INVALID_EQUIPMENT_TYPE: 'INVALID_EQUIPMENT_TYPE',
INVALID_EFFECT: 'INVALID_EFFECT',
EFFECT_NOT_ALLOWED: 'EFFECT_NOT_ALLOWED',
STACKS_EXCEED_MAX: 'STACKS_EXCEED_MAX',
INSUFFICIENT_CAPACITY: 'INSUFFICIENT_CAPACITY',
EQUIPMENT_NOT_FOUND: 'EQUIPMENT_NOT_FOUND',
DESIGN_NOT_FOUND: 'DESIGN_NOT_FOUND',
EQUIPMENT_NOT_PREPARED: 'EQUIPMENT_NOT_PREPARED',
ALREADY_PREPARED: 'ALREADY_PREPARED',
// Action state errors
INVALID_ACTION_STATE: 'INVALID_ACTION_STATE',
// Prestige errors
INSUFFICIENT_INSIGHT: 'INSUFFICIENT_INSIGHT',
GUARDIAN_NOT_DEFEATED: 'GUARDIAN_NOT_DEFEATED',
PACT_ALREADY_SIGNED: 'PACT_ALREADY_SIGNED',
PACT_SLOTS_FULL: 'PACT_SLOTS_FULL',
RITUAL_IN_PROGRESS: 'RITUAL_IN_PROGRESS',
PRESTIGE_MAX_LEVEL: 'PRESTIGE_MAX_LEVEL',
INVALID_PRESTIGE_ID: 'INVALID_PRESTIGE_ID',
// General
INVALID_INPUT: 'INVALID_INPUT',
NOT_INITIALIZED: 'NOT_INITIALIZED',
} as const;
export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
/**
* Standard result type for operations that can fail.
* T is the success payload type.
*/
export type Result<T = void> =
| { success: true; data: T }
| { success: false; error: string; code: ErrorCodeType };
/** Create a success result. */
export function ok<T>(data: T): Result<T> {
return { success: true, data };
}
/** Create a void success result. */
export function okVoid(): Result<void> {
return { success: true, data: undefined };
}
/** Create a failure result. */
export function fail(code: ErrorCodeType, error: string): Result<never> {
return { success: false, error, code };
}
/** Create a failure result with a typed data field. */
export function failTyped<T>(code: ErrorCodeType, error: string): Result<T> {
return { success: false, error, code };
}
/**
* Unwrap a result, returning the data or a default value.
*/
export function unwrapOr<T>(result: Result<T>, defaultValue: T): T {
return result.success ? result.data : defaultValue;
}
/**
* Check if a result is a failure with a specific error code.
*/
export function isErrorCode<T>(result: Result<T>, code: ErrorCodeType): boolean {
return !result.success && result.code === code;
}