///|
pub extend Pty with @async/io.Writer::{write_once, write, write_reader}

///|
pub extend Pty with @async/io.Reader::{
  read,
  read_some,
  drop,
  read_all,
  read_until,
  read_exactly,
}

///|
#deprecated("internal")
#doc(hidden)
pub extend Pty with @async/io.Reader::{_direct_read, _get_internal_buffer}

///|
// Wait for `pid` while `background` runs alongside (close the terminal, grace
// period, hard kill, ...). The waiter is a spawned task on purpose, never the
// main task of the group: if the runtime cancels the waiter directly
// (`EventLoop::cleanup` after a fatal error in the host event loop), the
// group fails with `Cancelled` instead of reaching `with_task_group`'s
// `result.unwrap()` on a `Done` group with no result, which aborts the whole
// process (moonbitlang/async task_group.mbt:268). See repro/ for both shapes.
// The waiter is spawned first so that it registers for the child before
// `background` starts tearing the terminal down.
//
// `on_waiter` is a test hook (pty_wbtest.mbt): it hands out the waiter task
// so a test can cancel it the way the runtime would.
async fn wait_pid_with(
  pid : Int,
  background : async () -> Unit,
  on_waiter? : (@async.Task[Int]) -> Unit,
) -> Int {
  @async.with_task_group() <| g => {
    let waiter = g.spawn(() => @async/process.wait_pid(pid))
    if on_waiter is Some(hook) {
      hook(waiter)
    }
    g.spawn_bg(no_wait=true, background)
    waiter.wait()
  }
}