chore: audit frontend error reporting to exclude expected behaviours #79

Merged
naomi merged 1 commits from chore/error-reporting-audit into main 2026-03-19 16:01:22 -07:00
+111 -103
View File
@@ -1224,9 +1224,12 @@ export const GameProvider = ({
) { ) {
signatureReference.current = null; signatureReference.current = null;
localStorage.removeItem("elysium_save_signature"); localStorage.removeItem("elysium_save_signature");
} else {
logError("auto_save", error_);
} }
/*
* Network failures during background auto-save are expected on
* flaky connections — the next tick will retry, so no telemetry needed
*/
}); });
} }
} }
@@ -1254,10 +1257,9 @@ export const GameProvider = ({
} }
await reloadReference.current(); await reloadReference.current();
}). }).
catch((error_: unknown) => { catch(() => {
logError("auto_prestige", error_);
/* Silently ignore — will retry next tick */ /* Silently ignore — eligibility is re-checked every tick */
}). }).
finally(() => { finally(() => {
isAutoPrestigingReference.current = false; isAutoPrestigingReference.current = false;
@@ -1307,11 +1309,18 @@ export const GameProvider = ({
}); });
}). }).
catch((error_: unknown) => { catch((error_: unknown) => {
logError("auto_boss", error_);
const message const message
= error_ instanceof Error = error_ instanceof Error
? error_.message ? error_.message
: String(error_); : String(error_);
/*
* "Boss is not currently available" is an expected race condition
* in the tick loop — suppress telemetry for this case only
*/
if (message !== "Boss is not currently available") {
logError("auto_boss", error_);
}
setAutoBossError(message); setAutoBossError(message);
setState((previous) => { setState((previous) => {
if (previous === null) { if (previous === null) {
@@ -1709,114 +1718,104 @@ export const GameProvider = ({
}, []); }, []);
const startExploration = useCallback(async(areaId: string) => { const startExploration = useCallback(async(areaId: string) => {
try { const response = await startExplorationApi({ areaId });
const response = await startExplorationApi({ areaId }); setState((previous) => {
if (previous?.exploration === undefined) {
return previous;
}
return {
...previous,
exploration: {
...previous.exploration,
areas: previous.exploration.areas.map((a) => {
return a.id === areaId
? {
...a,
endsAt: response.endsAt,
status: "in_progress" as const,
}
: a;
}),
},
};
});
}, []);
const collectExploration = useCallback(
async(areaId: string): Promise<ExploreCollectResponse> => {
const result = await collectExplorationApi({ areaId });
setState((previous) => { setState((previous) => {
if (previous?.exploration === undefined) { if (previous?.exploration === undefined) {
return previous; return previous;
} }
let materials = [ ...previous.exploration.materials ];
// Apply material drops from the random loot roll
for (const drop of result.materialsFound) {
const existing = materials.find((mat) => {
return mat.materialId === drop.materialId;
});
if (existing === undefined) {
materials = [
...materials,
{ materialId: drop.materialId, quantity: drop.quantity },
];
} else {
materials = materials.map((mat) => {
return mat.materialId === drop.materialId
? { ...mat, quantity: mat.quantity + drop.quantity }
: mat;
});
}
}
// Apply material from event (if any)
const materialGained = result.event?.materialGained;
if (materialGained !== null && materialGained !== undefined) {
const { materialId, quantity } = materialGained;
const existing = materials.find((mat) => {
return mat.materialId === materialId;
});
if (existing === undefined) {
materials = [ ...materials, { materialId, quantity } ];
} else {
materials = materials.map((mat) => {
return mat.materialId === materialId
? { ...mat, quantity: mat.quantity + quantity }
: mat;
});
}
}
return { return {
...previous, ...previous,
exploration: { exploration: {
...previous.exploration, ...previous.exploration,
areas: previous.exploration.areas.map((a) => { areas: previous.exploration.areas.map((a) => {
return a.id === areaId return a.id === areaId
? { ? { ...a, completedOnce: true, status: "available" as const }
...a,
endsAt: response.endsAt,
status: "in_progress" as const,
}
: a; : a;
}), }),
materials: materials,
},
player: {
...previous.player,
totalGoldEarned:
previous.player.totalGoldEarned
+ Math.max(0, result.event?.goldChange ?? 0),
},
resources: {
...previous.resources,
essence:
previous.resources.essence + (result.event?.essenceChange ?? 0),
gold: Math.max(
0,
previous.resources.gold + (result.event?.goldChange ?? 0),
),
}, },
}; };
}); });
} catch (error_: unknown) { return result;
logError("start_exploration", error_);
throw error_;
}
}, []);
const collectExploration = useCallback(
async(areaId: string): Promise<ExploreCollectResponse> => {
try {
const result = await collectExplorationApi({ areaId });
setState((previous) => {
if (previous?.exploration === undefined) {
return previous;
}
let materials = [ ...previous.exploration.materials ];
// Apply material drops from the random loot roll
for (const drop of result.materialsFound) {
const existing = materials.find((mat) => {
return mat.materialId === drop.materialId;
});
if (existing === undefined) {
materials = [
...materials,
{ materialId: drop.materialId, quantity: drop.quantity },
];
} else {
materials = materials.map((mat) => {
return mat.materialId === drop.materialId
? { ...mat, quantity: mat.quantity + drop.quantity }
: mat;
});
}
}
// Apply material from event (if any)
const materialGained = result.event?.materialGained;
if (materialGained !== null && materialGained !== undefined) {
const { materialId, quantity } = materialGained;
const existing = materials.find((mat) => {
return mat.materialId === materialId;
});
if (existing === undefined) {
materials = [ ...materials, { materialId, quantity } ];
} else {
materials = materials.map((mat) => {
return mat.materialId === materialId
? { ...mat, quantity: mat.quantity + quantity }
: mat;
});
}
}
return {
...previous,
exploration: {
...previous.exploration,
areas: previous.exploration.areas.map((a) => {
return a.id === areaId
? { ...a, completedOnce: true, status: "available" as const }
: a;
}),
materials: materials,
},
player: {
...previous.player,
totalGoldEarned:
previous.player.totalGoldEarned
+ Math.max(0, result.event?.goldChange ?? 0),
},
resources: {
...previous.resources,
essence:
previous.resources.essence + (result.event?.essenceChange ?? 0),
gold: Math.max(
0,
previous.resources.gold + (result.event?.goldChange ?? 0),
),
},
};
});
return result;
} catch (error_: unknown) {
logError("collect_exploration", error_);
throw error_;
}
}, },
[], [],
); );
@@ -1960,11 +1959,20 @@ export const GameProvider = ({
}); });
setBattleResult({ bossName: boss.name, result: result }); setBattleResult({ bossName: boss.name, result: result });
} catch (error_: unknown) { } catch (error_: unknown) {
logError("challenge_boss", error_); const bossErrorMessage
setBossError( = error_ instanceof Error
error_ instanceof Error
? error_.message ? error_.message
: "Failed to challenge boss", : "Failed to challenge boss";
/*
* "Boss is not currently available" is an expected server rejection
* (race condition between UI state and server state) — suppress telemetry
*/
if (bossErrorMessage !== "Boss is not currently available") {
logError("challenge_boss", error_);
}
setBossError(
bossErrorMessage,
); );
} }
}, [ forceSync ]); }, [ forceSync ]);