Skip to content

Commit 859f30e

Browse files
authored
fix(webapp): report message catalogs survive the production bundle (#4488)
GET /api/v1/reports/health threw `no catalog registered for report "health"` in production (fine in dev): the catalog registered itself as a side effect of a bare import, which the SSR build tree-shakes under `"sideEffects": false`. Verified on the built server bundle — main's is missing the catalog, this branch's carries it. Fix: catalogs are values on the report registry entries; the resolver reads them from there and the mutable register-at-import step is gone.
1 parent 763b5dc commit 859f30e

5 files changed

Lines changed: 23 additions & 13 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Fix the health report failing with an internal error when requested through the API.

apps/webapp/app/presenters/v3/reports/health/health-messages.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* metrics / evidence — meaning lives here, numbers stay facts.
99
*/
1010

11-
import { registerReportMessages, type ReportMessages } from "../report-messages";
11+
import { type ReportMessages } from "../report-messages";
1212
import { type ReasonCode, type Severity } from "../report-view-model";
1313

1414
/** Metric id -> expanded display label. */
@@ -166,5 +166,3 @@ export const healthMessages: ReportMessages = {
166166
statementMessage,
167167
actionMessage: (code) => ACTIONS[code] ?? code,
168168
};
169-
170-
registerReportMessages("health", healthMessages);

apps/webapp/app/presenters/v3/reports/health/health.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import { applyFlowPolicy, buildFlowRead, interpretFlow } from "./flow";
2626
import { interpretLiveness } from "./liveness";
2727
// Registers the "health" message catalog (side effect) so the renderer resolves this report's
2828
// codes. Kept here — the health report's entry module — so loading it always registers its prose.
29-
import "./health-messages";
3029

3130
// Re-exported so the data layer + tests keep a single import path (`./health`).
3231
export { HEALTH_THRESHOLDS, isPendingIncreasing, type HealthInput } from "./health-core";
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Catalogs by value, in a module that imports ONLY the per-report `*-messages`
3+
* files — no loaders, no IO. Presentation stays decoupled from the data layer,
4+
* and a value import can't be tree-shaken away.
5+
*/
6+
import { healthMessages } from "./health/health-messages";
7+
import { type ReportMessages } from "./report-messages";
8+
9+
export const REPORT_MESSAGE_CATALOGS: Record<string, ReportMessages> = {
10+
health: healthMessages,
11+
};

apps/webapp/app/presenters/v3/reports/report-messages.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* No report vocabulary here — that would re-couple the renderer to a specific report.
66
*/
77

8+
import { REPORT_MESSAGE_CATALOGS } from "./report-message-catalogs";
89
import { type ReasonCode, type Severity } from "./report-view-model";
910

1011
/**
@@ -22,16 +23,11 @@ export type ReportMessages = {
2223
actionMessage(code: ReasonCode): string;
2324
};
2425

25-
const catalogs = new Map<string, ReportMessages>();
26-
27-
/** Register a report's catalog under its title (e.g. "health"). Called for its side effect. */
28-
export function registerReportMessages(title: string, messages: ReportMessages): void {
29-
catalogs.set(title, messages);
30-
}
31-
32-
/** Look up a report's catalog by `vm.title`. Throws if the report never registered one. */
26+
/** Look up a report's catalog by `vm.title`. Catalogs are values, never
27+
* registered at import time — side-effect registration is what the production
28+
* bundle tree-shakes away. */
3329
export function reportMessages(title: string): ReportMessages {
34-
const messages = catalogs.get(title);
30+
const messages = REPORT_MESSAGE_CATALOGS[title];
3531
if (!messages) {
3632
throw new Error(`report-messages: no catalog registered for report "${title}"`);
3733
}

0 commit comments

Comments
 (0)