function closeWithGrace
closeWithGrace(
closes: Readonly<Record<string, Promise<unknown> | null | undefined>>,
graceMs?: number
): Promise<Abandoned>

Awaits every cleanup promise in closes, but never longer than graceMs. Resolves whichever happens first: every close settles (Promise.allSettled absorbs rejections so a single failing close cannot wedge the others), or the grace timer fires. Pending closes keep running in the background after a timeout — the caller is expected to process.exit() shortly after, which terminates them anyway.

On timeout, writes one line to stderr so the user sees that shutdown was cut short. This is an exceptional condition — not verbose output — so it fires regardless of --debug: every user who hits the deadlock deserves to know browser/server cleanup may have left orphans. Goes to stderr so it never lands in the TAP stream.

null / undefined entries are accepted as-is so optional-chained closes such as connections.server?.close() flow in without per-call filtering. Keyed rather than positional so the timeout can name what it gave up on.

Resolves with what it abandoned: the names, and settled — a promise for the moment those finally finish. Giving up on a close is not the same as being done with it, and a caller that outlives this one (a watch session restarting) has to be able to come back for it. Without that, an abandoned browser close holds its transport open for the life of the process, and nothing ever looks at it again.

const browserClose = Promise.resolve();
const serverClose: Promise<void> | undefined = undefined; // e.g. connections.server?.close()

(await closeWithGrace({ browser: browserClose, server: serverClose })).names; // [] — all settled
(await closeWithGrace({ wedged: Promise.reject(new Error('x')) }, 50)).names; // [] — a rejection settles

Parameters

closes: Readonly<Record<string, Promise<unknown> | null | undefined>>
optional
graceMs: number = CLEANUP_GRACE_MS

Return Type

Promise<Abandoned>

Usage

import { closeWithGrace } from "lib/utils/close-with-grace.ts";