diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c248c1..32270293 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Changes + +- **Desloppify mechanical cleanup** — closes three filed issues in one PR. (1) Adds JSDoc `@param` descriptions to `CascadeLog.jsx` (`props`) and `seasonAnalytics.mjs` (`opts`), clearing the two `jsdoc/require-param-description` lint warnings (#400). (2) Renames the cross-file `makeFactionMap` test helpers — `EventCard.test.jsx` uses `makeSectorMap` (it builds a per-faction sectors map for one event card) and `DashboardClient.test.jsx` uses `makeDashboardMap` (it builds the full mapState shape including region 0/11) — each helper is a genuinely different shape (regions 1-11 vs 1-10 vs 0-11 with different field sets), so renaming is more honest than extracting a fake shared abstraction; the third occurrence in `computeFrontier.test.mjs` is already scoped inside a `describe()` block (#401). (3) Annotates the two intentional empty-`catch` swallows in `MinistryProvider.jsx` (flicker scheduler) and `useLiveData.mjs` (`saveCachedState`) — both now capture `err` and `console.debug` it so the swallow is visible during diagnosis, with the existing rationale promoted from inline comment to a fuller multi-line explanation of why each path is non-critical (#399). No runtime behavior change. + ## 0.51.3 ### Changes diff --git a/src/__tests__/unit/features/dashboard/DashboardClient.test.jsx b/src/__tests__/unit/features/dashboard/DashboardClient.test.jsx index 48959c7a..38c84b5a 100644 --- a/src/__tests__/unit/features/dashboard/DashboardClient.test.jsx +++ b/src/__tests__/unit/features/dashboard/DashboardClient.test.jsx @@ -65,7 +65,7 @@ vi.mock('@/features/stats/evaluateProgress.mjs', () => ({ import DashboardClient from '@/features/dashboard/DashboardClient'; -function makeFactionMap(overrides = {}) { +function makeDashboardMap(overrides = {}) { const map = {}; for (let r = 0; r <= 11; r++) { map[r] = { region: `Region ${r}`, status: 'lost', event: 'idle', percent: 0 }; @@ -77,10 +77,10 @@ function makeFactionMap(overrides = {}) { } const baseMapState = { - 0: makeFactionMap(), - 1: makeFactionMap(), - 2: makeFactionMap(), - 3: makeFactionMap(), // Super Earth + 0: makeDashboardMap(), + 1: makeDashboardMap(), + 2: makeDashboardMap(), + 3: makeDashboardMap(), // Super Earth }; function getCardProps(testId) { @@ -306,7 +306,7 @@ describe('DashboardClient — homeworld card suppression', () => { }, mapState: { ...baseMapState, - 0: makeFactionMap({ + 0: makeDashboardMap({ 11: { event: 'active', status: 'active', diff --git a/src/__tests__/unit/features/galaxy/EventCard.test.jsx b/src/__tests__/unit/features/galaxy/EventCard.test.jsx index 46c9823d..a9b46307 100644 --- a/src/__tests__/unit/features/galaxy/EventCard.test.jsx +++ b/src/__tests__/unit/features/galaxy/EventCard.test.jsx @@ -25,7 +25,7 @@ const baseProps = { * Build a mapState[factionIndex] with the given sector statuses. * Default: everything lost. Each entry in `overrides` keyed by region 1–11. */ -function makeFactionMap(overrides = {}) { +function makeSectorMap(overrides = {}) { const map = {}; for (let r = 1; r <= 11; r++) map[r] = { status: 'lost', percent: 0 }; for (const [key, val] of Object.entries(overrides)) { @@ -258,7 +258,7 @@ describe('EventCard (campaign view)', () => { const campaignProps = { ...baseProps, view: 'campaign', - factionMap: makeFactionMap({ + factionMap: makeSectorMap({ 1: { status: 'captured', percent: 100 }, 2: { status: 'captured', percent: 100 }, 3: { status: 'captured', percent: 100 }, @@ -286,7 +286,7 @@ describe('EventCard (campaign view)', () => { { , @@ -355,7 +355,7 @@ describe('EventCard (campaign view)', () => { , diff --git a/src/features/ministry/MinistryProvider.jsx b/src/features/ministry/MinistryProvider.jsx index dbf80fb9..efa0a9e0 100644 --- a/src/features/ministry/MinistryProvider.jsx +++ b/src/features/ministry/MinistryProvider.jsx @@ -189,8 +189,11 @@ export default function MinistryProvider({ warTone, children }) { ]; const dur = randomBetween(FLICKER_DUR_MIN_MS, FLICKER_DUR_MAX_MS, rng); entry.onFlicker(charIdx, dur); - } catch { - // swallow; reschedule below + } catch (err) { + // Swallow flicker-scheduling errors and reschedule below. + // The flicker animation is a cosmetic non-critical path; we + // never want a transient timing issue to crash the provider. + console.debug('[MinistryProvider] flicker scheduling failed:', err); } scheduleNext(); } diff --git a/src/features/timeline/CascadeLog.jsx b/src/features/timeline/CascadeLog.jsx index 30d487ac..0305c28d 100644 --- a/src/features/timeline/CascadeLog.jsx +++ b/src/features/timeline/CascadeLog.jsx @@ -11,7 +11,7 @@ import { groupCascadesBySeason } from '@/features/timeline/groupCascadesBySeason * Cross-season cascade log. Same section layout as EventLog, grouped by * season instead of by day. Renders nothing when `cascades` is empty. * - * @param {object} props + * @param {object} props - Component props. * @param {Array} props.cascades - Each cascade includes a `season` field. * @param {string} [props.lede] - Optional one-line summary above the groups. * @param {string} [props.title] - Optional heading text (defaults to "Cascade Failures"). diff --git a/src/shared/hooks/useLiveData.mjs b/src/shared/hooks/useLiveData.mjs index b6707c0f..2f1db070 100644 --- a/src/shared/hooks/useLiveData.mjs +++ b/src/shared/hooks/useLiveData.mjs @@ -37,8 +37,10 @@ function saveCachedState(data, mapState) { CACHE_KEY, JSON.stringify({ data, mapState, ts: Date.now() }), ); - } catch { - // localStorage full or unavailable — ignore + } catch (err) { + // localStorage full / unavailable / disabled in private mode — ignore. + // The cache is an offline-fallback nicety, never a correctness path. + console.debug('[useLiveData] saveCachedState skipped:', err); } } diff --git a/src/shared/utils/game/seasonAnalytics.mjs b/src/shared/utils/game/seasonAnalytics.mjs index 8687e3a8..5cc738ca 100644 --- a/src/shared/utils/game/seasonAnalytics.mjs +++ b/src/shared/utils/game/seasonAnalytics.mjs @@ -10,8 +10,8 @@ const MAX_GAP_SEC = 3600; // 1 hour * and consecutive events within `MAX_GAP_SEC` (1 hour). * * @param {Array} events - h1_event records (any type, any status) - * @param {object} [opts] - * @param {number} [opts.minLength=3] - Inclusive minimum cascade length + * @param {object} [opts] - Optional configuration. + * @param {number} [opts.minLength=3] - Inclusive minimum cascade length. * @returns {Array<{ * length: number, * faction: string,