DigitalConsumer.FixMyGlass/src/router/methods/before-each-error-recovery.js
Matt Sykes 76b4c35d59 Prevent error redirect loops with beforeEach circuit breaker
This adds a circuit breaker for uncaught failures in beforeEach. The first failure still goes through the normal ERROR/RESTART recovery path; a second consecutive failure redirects to the static error page instead of looping.
- New before-each-error-recovery module tracks recovery attempts in sessionStorage (with an in-memory fallback).
- ERROR/RESTART routes skip normal guard logic so the counter can accumulate across bounce-backs.
- The counter clears only after a fully successful navigation.
- redirectToStaticErrorPage centralizes hard-error bailout: logs, clears poisoned sessionStorage keys (submittedState, heritage redirect count, recovery count, externalParameterState), then navigates to /fmg/static/error.
- The static error page RESTART flow clears the same sessionStorage keys so bad state does not re-enter the funnel.
- Session storage key strings are centralized in session-storage.js.
- Unit tests cover recovery counting, bailout clearing, and navigation fallbacks.
Expected impact: Stops repeated ERROR → RESTART → ERROR cycles (including corrupt submittedState JSON.parse failures and recurring automation errors in us-east-1/us-east-2) by bailing out to the static error page after one failed recovery attempt.
2026-06-10 15:48:29 -04:00

51 lines
1.5 KiB
JavaScript

import { sessionStorageKeyConstants } from "@/constants/session-storage";
const recoveryKey = sessionStorageKeyConstants.BEFORE_EACH_ERROR_RECOVERY_COUNT;
/** Used when sessionStorage is missing, throws, or does not persist (e.g. some bots). */
let inMemoryRecoveryCount = 0;
export function getBeforeEachErrorRecoveryCount() {
try {
const stored = window.sessionStorage?.getItem(recoveryKey);
if (stored !== null && stored !== undefined) {
return Number(stored) || 0;
}
} catch {
// sessionStorage unavailable; use in-memory fallback
}
return inMemoryRecoveryCount;
}
export function incrementBeforeEachErrorRecoveryCount() {
const nextCount = getBeforeEachErrorRecoveryCount() + 1;
inMemoryRecoveryCount = nextCount;
try {
window.sessionStorage?.setItem(recoveryKey, String(nextCount));
} catch {
// in-memory count already updated
}
return nextCount;
}
export function clearBeforeEachErrorRecoveryCount() {
inMemoryRecoveryCount = 0;
try {
window.sessionStorage?.removeItem(recoveryKey);
} catch {
// in-memory count already cleared
}
}
export function hasBeforeEachErrorRecoveryBeenAttempted() {
return getBeforeEachErrorRecoveryCount() >= 1;
}
/** Test-only: resets in-memory and sessionStorage recovery state between unit tests. */
export function resetBeforeEachErrorRecoveryStateForTests() {
clearBeforeEachErrorRecoveryCount();
}