49f8de01ca
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)
90 lines
2.9 KiB
TypeScript
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;
|
|
}
|