PipeTransport.js 2.94 KB
/**
 * PipeTransport — 通过 stdin/stdout 管道通信
 *
 * 接收 Launcher 返回的 { stdin, stdout, stderr, process } 句柄,
 * 绑定事件,使用基类 _handleData 做 NDJSON 解析。
 *
 * 职责边界:
 *   - ✅ 消息收发(stdin 写入 / stdout 读取)
 *   - ✅ 事件监听管理(connect 时绑定 / close 时解绑)
 *   - ❌ 进程生命周期(启动/终止由 Launcher 负责)
 */

const { Transport } = require('./Transport');

class PipeTransport extends Transport {
  constructor() {
    super();
    this._stdin = null;
    this._process = null;
  }

  get isConnected() {
    return this._stdin !== null && !this._stdin.destroyed;
  }

  /**
   * 绑定到已有的 stdin/stdout/stderr 流
   *
   * @param {object} handle - Launcher.start() 返回的句柄
   * @param {Writable} handle.stdin
   * @param {Readable} handle.stdout
   * @param {Readable} [handle.stderr]
   * @param {ChildProcess} [handle.process]
   */
  async connect(handle) {
    this._stdin = handle.stdin;
    this._process = handle.process || null;

    // stdout → NDJSON 解析
    handle.stdout.on('data', (data) => {
      this._handleData(data);
    });

    // stderr 转发
    if (handle.stderr) {
      handle.stderr.on('data', (data) => {
        this.emit('stderr', data.toString());
      });
    }

    // 进程退出
    if (handle.process) {
      handle.process.on('exit', (code, signal) => {
        this.emit('close', code, signal);
      });
      handle.process.on('error', (err) => {
        this.emit('error', err);
      });
    }
  }

  /**
   * 发送消息
   */
  send(msg) {
    if (!this.isConnected) {
      throw new Error('[PipeTransport] Transport is closed');
    }
    // 支持传入 string 或 object
    const str = typeof msg === 'string' ? msg : JSON.stringify(msg) + '\n';
    this._stdin.write(str);
  }

  /**
   * 关闭 stdin(发送 EOF)— 用于一次性输入场景
   *
   * 调用后不再可写入,但 stdout/stderr 仍可接收输出。
   */
  endInput() {
    if (this._stdin && !this._stdin.destroyed) {
      this._stdin.end();
    }
  }

  /**
   * 关闭连接 — 清理 PipeTransport 自身绑定的事件监听器
   *
   * 注意:不终止子进程。进程生命周期由 Launcher.stop() 管理。
   * 这里只解除 connect() 时在 stdout/stderr/process 上注册的监听器,
   * 避免已关闭的 transport 继续接收事件触发回调。
   */
  close() {
    if (this._process) {
      try {
        // 解除 connect() 中在底层流上绑定的事件监听器
        if (this._process.stdout) this._process.stdout.removeAllListeners('data');
        if (this._process.stderr) this._process.stderr.removeAllListeners('data');
        this._process.removeAllListeners('exit');
        this._process.removeAllListeners('error');
      } catch { }
      this._process = null;
    }
    this._stdin = null;
    this.buffer = '';
    this.removeAllListeners();
  }
}

module.exports = { PipeTransport };