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();
    },
  };
}