fix(bonjour): suppress ciao internal cancellations

This commit is contained in:
Peter Steinberger
2026-04-29 00:59:40 +01:00
parent bd11678122
commit 27599d319e
2 changed files with 25 additions and 2 deletions

View File

@@ -315,7 +315,7 @@ describe("gateway bonjour advertiser", () => {
await started.stop();
});
it("does not install process-level ciao handlers by default", async () => {
it("installs only the scoped ciao unhandled-rejection listener by default", async () => {
enableAdvertiserUnitMode();
const destroy = vi.fn().mockResolvedValue(undefined);
@@ -331,7 +331,7 @@ describe("gateway bonjour advertiser", () => {
{ logger },
);
expect(processOn).not.toHaveBeenCalledWith("unhandledRejection", expect.any(Function));
expect(processOn).toHaveBeenCalledWith("unhandledRejection", expect.any(Function));
expect(processOn).not.toHaveBeenCalledWith("uncaughtException", expect.any(Function));
await started.stop();

View File

@@ -69,6 +69,7 @@ type ServiceStateTracker = {
type ConsoleLogFn = (...args: unknown[]) => void;
type UncaughtExceptionHandler = (error: unknown) => boolean;
type UnhandledRejectionHandler = (reason: unknown) => boolean;
type ProcessUnhandledRejectionListener = (reason: unknown, promise: Promise<unknown>) => void;
type ExecBridge = (command: string, options?: unknown, callback?: unknown) => ChildProcess;
type ExecOptionsRecord = Record<string, unknown> & { windowsHide?: boolean };
@@ -324,6 +325,25 @@ function installCiaoWindowsExecHidePatch(): () => void {
};
}
function installCiaoUnhandledRejectionListener(handler: UnhandledRejectionHandler): () => void {
const hadOtherListeners = process.listenerCount("unhandledRejection") > 0;
const listener: ProcessUnhandledRejectionListener = (reason) => {
if (handler(reason)) {
return;
}
if (hadOtherListeners) {
return;
}
queueMicrotask(() => {
throw reason instanceof Error ? reason : new Error(String(reason));
});
};
process.on("unhandledRejection", listener);
return () => {
process.off("unhandledRejection", listener);
};
}
export async function startGatewayBonjourAdvertiser(
opts: GatewayBonjourAdvertiseOpts,
deps: BonjourAdvertiserDeps = {},
@@ -341,6 +361,7 @@ export async function startGatewayBonjourAdvertiser(
let restoreConsoleLog: () => void = () => {};
let requestCiaoRecovery: ((classification: CiaoProcessErrorClassification) => void) | undefined;
let cleanupUnhandledRejection: (() => void) | undefined;
let cleanupDirectUnhandledRejection: (() => void) | undefined;
let cleanupUncaughtException: (() => void) | undefined;
let processHandlersCleaned = false;
@@ -349,6 +370,7 @@ export async function startGatewayBonjourAdvertiser(
return;
}
processHandlersCleaned = true;
cleanupDirectUnhandledRejection?.();
cleanupUncaughtException?.();
cleanupUnhandledRejection?.();
}
@@ -380,6 +402,7 @@ export async function startGatewayBonjourAdvertiser(
}
return true;
};
cleanupDirectUnhandledRejection = installCiaoUnhandledRejectionListener(handleCiaoProcessError);
cleanupUnhandledRejection = deps.registerUnhandledRejectionHandler?.(handleCiaoProcessError);
cleanupUncaughtException = deps.registerUncaughtExceptionHandler?.(handleCiaoProcessError);