///|
/// JS target: read a line from stdin synchronously using Node.js fs.readSync.
/// Returns empty string if EOF or error.
/// Handles encoding: tries UTF-8 first, falls back to GBK on Windows.
pub fn read_line() -> String {
  js_read_line_sync()
}

///|
extern "js" fn js_read_line_sync() -> String =
  #|() => {
  #|  try {
  #|    const fs = require('fs');
  #|    const buf = Buffer.alloc(4096);
  #|    const n = fs.readSync(0, buf, 0, 4096, null);
  #|    if (n === 0) return '';
  #|    // Try UTF-8 first
  #|    let text = buf.toString('utf-8', 0, n);
  #|    // If replacement characters found, input was likely GBK (Windows codepage 936)
  #|    if (text.includes('\ufffd')) {
  #|      try {
  #|        // Use TextDecoder for GBK fallback
  #|        text = new TextDecoder('gbk', { fatal: false }).decode(buf.subarray(0, n));
  #|      } catch (_) {
  #|        // If gbk decoder unavailable, return the raw latin1 bytes as-is
  #|        text = buf.toString('latin1', 0, n);
  #|      }
  #|    }
  #|    return text.replace(/\r?\n$/, '');
  #|  } catch (e) {
  #|    return '';
  #|  }
  #|}

///|
/// Print a string without trailing newline (for prompts and streaming).
pub fn print(s : String) -> Unit {
  js_print(s)
}

///|
extern "js" fn js_print(s : String) -> Unit =
  #|(s) => { process.stdout.write(s); }