diff --git a/CHANGES.md b/CHANGES.md index a9216a3..145c3f8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## Unreleased +- `@openstatus/health`: `onReport` catches rejected promises from other JavaScript + realms without an unhandled rejection. - Health reports and responder fallbacks remain available when a rejected value cannot convert to a string. The error uses generic text without private fields. - Health responses ignore a top-level `toJSON` from `extend` so JSON diff --git a/packages/health/src/check.test.ts b/packages/health/src/check.test.ts index cba1530..5532b9f 100644 --- a/packages/health/src/check.test.ts +++ b/packages/health/src/check.test.ts @@ -1,6 +1,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import { setTimeout as delay } from "node:timers/promises"; +import { runInNewContext } from "node:vm"; import { createHealthCheck } from "./check.ts"; import { DuplicateProbeError } from "./errors.ts"; import type { Probe } from "./types.ts"; @@ -150,6 +151,20 @@ test("createHealthCheck() swallows onReport errors", async () => { assert.equal((await rejecting.report()).status, "ok"); }); +test("createHealthCheck() swallows onReport rejections from another realm", async () => { + const { probe } = counting("a"); + const ForeignPromise: PromiseConstructor = runInNewContext("Promise"); + assert.notEqual(ForeignPromise, Promise); + const check = createHealthCheck({ + probes: [probe], + onReport: () => ForeignPromise.reject(new Error("logger down")), + }); + const report = await check.report(); + assert.equal(report.status, "ok"); + await delay(0); + assert.equal(await check.report(), report); +}); + test("createHealthCheck() accepts the formatError presets", async () => { const { probe } = counting("a", true); const generic = await createHealthCheck({ probes: [probe] }).report(); diff --git a/packages/health/src/check.ts b/packages/health/src/check.ts index 8628c0f..57e02e6 100644 --- a/packages/health/src/check.ts +++ b/packages/health/src/check.ts @@ -59,8 +59,7 @@ export function createHealthCheck(options: HealthCheckOptions): HealthCheck { function notify(onReport: OnReport | undefined, report: HealthReport): void { if (onReport == null) return; try { - const result = onReport(report); - if (result instanceof Promise) result.catch(() => {}); + Promise.resolve(onReport(report)).catch(() => {}); } catch { return; }