node.mjs raw

   1  // MoxieJS Runtime — Node.js stdio bridge.
   2  // Implements smesh.lol/web/common/jsbridge/node — line-oriented stdin/stdout.
   3  // Only meaningful under Node; in browser/SW targets these are no-ops.
   4  
   5  // Convert a Moxie string (which may be a JS string or a {$array,$offset,$length}
   6  // slice shape depending on how the binding crosses the boundary) to a plain JS
   7  // string. types.mjs intentionally does not export this; everything we need is
   8  // visible in the slice shape itself.
   9  function _stringify(s) {
  10    if (typeof s === 'string') return s;
  11    if (s && typeof s.$length === 'number' && s.$array) {
  12      let out = '';
  13      for (let i = 0; i < s.$length; i++) out += String.fromCharCode(s.$array[s.$offset + i]);
  14      return out;
  15    }
  16    return String(s);
  17  }
  18  
  19  let _lineHandler = null;
  20  let _closeHandler = null;
  21  let _wired = false;
  22  
  23  function _wireStdinOnce() {
  24    if (_wired) return;
  25    _wired = true;
  26  
  27    // Node-only.
  28    if (typeof process === 'undefined' || !process.stdin) return;
  29  
  30    let buf = '';
  31    process.stdin.setEncoding('utf8');
  32    process.stdin.on('data', (chunk) => {
  33      buf += chunk;
  34      let nl;
  35      while ((nl = buf.indexOf('\n')) >= 0) {
  36        const line = buf.slice(0, nl);
  37        buf = buf.slice(nl + 1);
  38        if (_lineHandler) {
  39          try { _lineHandler(line); } catch (e) { console.error('OnLine handler threw:', e); }
  40        }
  41      }
  42    });
  43    process.stdin.on('end', () => {
  44      // Flush any trailing unterminated line.
  45      if (buf.length > 0 && _lineHandler) {
  46        try { _lineHandler(buf); } catch (e) { console.error('OnLine handler threw:', e); }
  47        buf = '';
  48      }
  49      if (_closeHandler) {
  50        try { _closeHandler(); } catch (e) { console.error('OnClose handler threw:', e); }
  51      }
  52    });
  53  }
  54  
  55  export function OnLine(fn) {
  56    _lineHandler = fn;
  57    _wireStdinOnce();
  58  }
  59  
  60  export function OnClose(fn) {
  61    _closeHandler = fn;
  62    _wireStdinOnce();
  63  }
  64  
  65  export function WriteLine(s) {
  66    const str = _stringify(s);
  67    if (typeof process !== 'undefined' && process.stdout) {
  68      process.stdout.write(str + '\n');
  69    } else {
  70      console.log(str);
  71    }
  72  }
  73  
  74  export function WriteErr(s) {
  75    const str = _stringify(s);
  76    if (typeof process !== 'undefined' && process.stderr) {
  77      process.stderr.write(str + '\n');
  78    } else {
  79      console.error(str);
  80    }
  81  }
  82  
  83  export function Exit(code) {
  84    if (typeof process !== 'undefined' && typeof process.exit === 'function') {
  85      process.exit(typeof code === 'number' ? code : 0);
  86    }
  87  }
  88  
  89  export function NowSeconds() {
  90    // Moxie side declares int64, so the boundary must emit BigInt to avoid
  91    // "Cannot mix BigInt and other types" at call sites that do int64 math.
  92    return BigInt(Math.floor(Date.now() / 1000));
  93  }
  94