File size: 1,688 Bytes
5cb63c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | import type { SpawnProcessAdapter } from "../types.js";
type ProcessEvents = Required<
Pick<SpawnProcessAdapter<NodeJS.Signals | null>, "onExit" | "onError">
>;
type ErrorListener = Parameters<ProcessEvents["onError"]>[0];
/** Preserve terminal facts and startup errors until the transport owner subscribes. */
export function createProcessAdapterEvents() {
let exit: { code: number | null; signal: NodeJS.Signals | null } | undefined;
const exitListeners = new Set<Parameters<ProcessEvents["onExit"]>[0]>();
const errorListeners = new Set<ErrorListener>();
const pendingErrors = new Map<Parameters<ErrorListener>[1], Error>();
return {
onExit: (listener: Parameters<ProcessEvents["onExit"]>[0]) => {
exitListeners.add(listener);
if (exit) {
listener(exit.code, exit.signal);
}
},
onError: (listener: ErrorListener) => {
errorListeners.add(listener);
for (const [source, error] of pendingErrors) {
listener(error, source);
}
pendingErrors.clear();
},
emitExit: (code: number | null, signal: NodeJS.Signals | null) => {
exit = { code, signal };
for (const listener of exitListeners) {
listener(code, signal);
}
},
emitError: (error: Error, source: Parameters<ErrorListener>[1]) => {
if (errorListeners.size === 0) {
if (!pendingErrors.has(source)) {
pendingErrors.set(source, error);
}
} else {
for (const listener of errorListeners) {
listener(error, source);
}
}
},
clear: () => {
exitListeners.clear();
errorListeners.clear();
pendingErrors.clear();
},
};
}
|