diff --git a/CHANGES.md b/CHANGES.md index 2099aa4..464810e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +- Health responses ignore a top-level `toJSON` from `extend` so JSON + serialization cannot replace the report fields. Nested dates and custom + JSON values keep their normal serialization. + ## 0.1.2 - Every hosting package (`fly`, `koyeb`, `railway`, `vercel`, `cloudflare`) diff --git a/packages/health/src/response.test.ts b/packages/health/src/response.test.ts index f93fd78..c11c729 100644 --- a/packages/health/src/response.test.ts +++ b/packages/health/src/response.test.ts @@ -76,3 +76,31 @@ test("renderHealthResponse() accepts interface-typed and Date fields", () => { '{"vitals":{"rss":1},"at":"2026-09-11T00:00:00.000Z","status":"ok","checkedAt":"2026-09-11T00:00:00.000Z"}', ); }); + +for (const exposeChecks of [true, false]) { + test(`renderHealthResponse() ignores top-level toJSON with exposeChecks=${exposeChecks}`, () => { + const extended = Object.freeze({ + toJSON: () => ({ status: "ok" }), + at: new Date("2026-09-11T00:00:00.000Z"), + server: { toJSON: () => ({ region: "fra" }) }, + }); + const res = renderHealthResponse( + report("unhealthy"), + { exposeChecks }, + extended, + ); + assert.equal(res.status, 503); + assert.deepEqual(JSON.parse(JSON.stringify(res.body)), { + at: "2026-09-11T00:00:00.000Z", + server: { region: "fra" }, + status: "unhealthy", + checkedAt: "2026-09-11T00:00:00.000Z", + ...(exposeChecks + ? { + latencyMs: 12, + checks: [{ name: "db", status: "ok", critical: true, latencyMs: 3 }], + } + : {}), + }); + }); +} diff --git a/packages/health/src/response.ts b/packages/health/src/response.ts index 2727acd..69887fb 100644 --- a/packages/health/src/response.ts +++ b/packages/health/src/response.ts @@ -42,6 +42,7 @@ export function renderHealthResponse( checks: report.checks, } : { ...extended, status: report.status, checkedAt: report.checkedAt }; + Reflect.deleteProperty(body, "toJSON"); return { status: statusCodeFor(report, options), headers: healthHeaders,