* fix(hopper): preserve caller abort reasons on cancellation Hopper cancellation dropped the caller's abort reason, and the typed error treated an explicit undefined cause as omission. Retain the exact reason at the request queue, startup, socket, and launcher boundaries. Co-authored-by: morluto <morluto@users.noreply.github.com> * style(hopper): format launcher cancellation return oxfmt rejects the wrapped abort return in HopperApplicationLauncher. Co-authored-by: morluto <morluto@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: morluto <morluto@users.noreply.github.com>
41 lines
1.2 KiB
TypeScript
41 lines
1.2 KiB
TypeScript
import { createConnection, type Socket } from "node:net";
|
|
|
|
import {
|
|
hopperCancelledBySignal,
|
|
type HopperCancelledError,
|
|
HopperStartError,
|
|
} from "../domain/hopperErrors.js";
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
|
|
/** Open one abort-aware Hopper Unix-socket connection attempt. */
|
|
export const connectHopperSocketOnce = (
|
|
socketPath: string,
|
|
signal: AbortSignal,
|
|
): Promise<Result<Socket, HopperStartError | HopperCancelledError>> =>
|
|
new Promise((resolve) => {
|
|
const socket = createConnection(socketPath);
|
|
const detach = (): void => {
|
|
socket.off("connect", onConnect);
|
|
socket.off("error", onError);
|
|
signal.removeEventListener("abort", onAbort);
|
|
};
|
|
const onConnect = (): void => {
|
|
detach();
|
|
resolve(ok(socket));
|
|
};
|
|
const onError = (cause: Error): void => {
|
|
detach();
|
|
socket.destroy();
|
|
resolve(err(new HopperStartError({ cause })));
|
|
};
|
|
const onAbort = (): void => {
|
|
detach();
|
|
socket.destroy();
|
|
resolve(err(hopperCancelledBySignal(signal)));
|
|
};
|
|
socket.once("connect", onConnect);
|
|
socket.once("error", onError);
|
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
if (signal.aborted) onAbort();
|
|
});
|