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