src/node/wrapper.ts/onExit
export function onMessage<M, T extends M>(
  proc: cp.ChildProcess | NodeJS.Process,
  fn: (message: M) => message is T,
  customLogger?: Logger,
): Promise<T> {
  return new Promise((resolve, reject) => {
    const cleanup = () => {
      proc.off("error", onError)
      proc.off("exit", onExit)
      proc.off("message", onMessage)
      clearTimeout(timeout)
    }

    const timeout = setTimeout(() => {
      cleanup()
      reject(new Error("timed out"))
    }, timeoutInterval)

    const onError = (error: Error) => {
      cleanup()
      reject(error)
    }

    const onExit = (code: number) => {
      cleanup()
      reject(new Error(`exited unexpectedly with code ${code}`))
    }

    const onMessage = (message: M) => {
      if (fn(message)) {
        cleanup()
        resolve(message)
      } else {
        ;(customLogger || logger).debug("got unhandled message", field("message", message))
      }
    }

    proc.on("message", onMessage)
    // NodeJS.Process doesn't have `error` but binding anyway shouldn't break
    // anything. It does have `exit` but the types aren't working.
    ;(proc as cp.ChildProcess).on("error", onError)
    ;(proc as cp.ChildProcess).on("exit", onExit)
  })
}