MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix tty teardown exit and align stdio encoding

+97 -18
+3 -7
src/modules/process.c
··· 664 664 if (!stdin_state.tty_initialized) { 665 665 uv_loop_t *loop = uv_default_loop(); 666 666 if (uv_tty_init(loop, &stdin_state.tty, STDIN_FILENO, 1) != 0) return; 667 - #ifndef _WIN32 668 - uv_tty_set_mode(&stdin_state.tty, tty_is_raw_mode(STDIN_FILENO) ? UV_TTY_MODE_RAW : UV_TTY_MODE_NORMAL); 669 - #endif 670 667 stdin_state.tty.data = NULL; 671 668 stdin_state.tty_initialized = true; 672 - } else { 673 - #ifndef _WIN32 674 - uv_tty_set_mode(&stdin_state.tty, tty_is_raw_mode(STDIN_FILENO) ? UV_TTY_MODE_RAW : UV_TTY_MODE_NORMAL); 675 - #endif 669 + uv_unref((uv_handle_t *)&stdin_state.tty); 676 670 } 671 + uv_ref((uv_handle_t *)&stdin_state.tty); 677 672 stdin_state.reading = true; 678 673 uv_read_start((uv_stream_t *)&stdin_state.tty, stdin_alloc_buffer, on_stdin_read); 679 674 } ··· 682 677 if (!stdin_state.reading) return; 683 678 uv_read_stop((uv_stream_t *)&stdin_state.tty); 684 679 stdin_state.reading = false; 680 + uv_unref((uv_handle_t *)&stdin_state.tty); 685 681 } 686 682 687 683 #ifndef _WIN32
-9
src/modules/tty.c
··· 197 197 tty_read_stream_emit_error(state, "open", rc); 198 198 return js_mkundef(); 199 199 } 200 - #ifndef _WIN32 201 - uv_tty_set_mode(&state->tty, tty_is_raw_mode(state->fd) ? UV_TTY_MODE_RAW : UV_TTY_MODE_NORMAL); 202 - #endif 203 200 state->tty.data = state; 204 201 state->initialized = true; 205 202 } ··· 727 724 728 725 static ant_value_t tty_read_stream_set_raw_mode(ant_t *js, ant_value_t *args, int nargs) { 729 726 ant_value_t this_obj = js_getthis(js); 730 - tty_read_stream_state_t *state = tty_read_stream_state_from_obj(this_obj); 731 727 if (!is_special_object(this_obj)) { 732 728 return js_mkerr_typed(js, JS_ERR_TYPE, "setRawMode() requires a ReadStream receiver"); 733 729 } ··· 737 733 if (!tty_set_raw_mode(fd, enable)) { 738 734 return js_mkerr_typed(js, JS_ERR_GENERIC, "Failed to set raw mode for fd %d", fd); 739 735 } 740 - #ifndef _WIN32 741 - if (state && state->initialized && !state->closing) { 742 - uv_tty_set_mode(&state->tty, enable ? UV_TTY_MODE_RAW : UV_TTY_MODE_NORMAL); 743 - } 744 - #endif 745 736 js_set(js, this_obj, "isRaw", js_bool(enable)); 746 737 return this_obj; 747 738 }
+1 -1
tests/amp/mini_amp_tui_repro.js
··· 170 170 if (!running) return; 171 171 running = false; 172 172 clearInterval(timer); 173 - process.stdout.write("\x1b[0m\x1b[2J\x1b[H"); 173 + process.stdout.write("\x1b[0m\x1b[2J\x1b[H\x1b[?25h"); 174 174 if (process.stdin.isTTY && typeof process.stdin.setRawMode === "function") { 175 175 process.stdin.setRawMode(false); 176 176 }
+1 -1
tests/amp/mini_amp_widget_tui_repro.js
··· 344 344 for (const ch of chunk) { 345 345 if (ch === "q" || ch === "\u0003") { 346 346 clearInterval(timer); 347 - process.stdout.write("\x1b[0m\x1b[2J\x1b[H"); 347 + process.stdout.write("\x1b[0m\x1b[2J\x1b[H\x1b[?25h"); 348 348 process.exit(0); 349 349 } 350 350 if (ch === "m") {
+92
tests/test_mini_amp_tui_exit_restores_tty.cjs
··· 1 + const { spawnSync } = require('child_process'); 2 + 3 + function fail(message) { 4 + throw new Error(message); 5 + } 6 + 7 + if (process.platform === 'win32') { 8 + console.log('skipping mini amp tty restore test on win32'); 9 + process.exit(0); 10 + } 11 + 12 + const script = ` 13 + import os, pty, select, signal, sys, termios, time 14 + 15 + exec_path = sys.argv[1] 16 + script_path = sys.argv[2] 17 + 18 + pid, master = pty.fork() 19 + if pid == 0: 20 + os.execv(exec_path, [exec_path, script_path]) 21 + 22 + sent = False 23 + exit_code = None 24 + deadline = time.time() + 8.0 25 + 26 + while time.time() < deadline: 27 + done, status = os.waitpid(pid, os.WNOHANG) 28 + if done == pid: 29 + exit_code = os.waitstatus_to_exitcode(status) 30 + break 31 + 32 + r, _, _ = select.select([master], [], [], 0.1) 33 + if master not in r: 34 + continue 35 + 36 + try: 37 + chunk = os.read(master, 4096) 38 + except OSError: 39 + break 40 + 41 + if not chunk: 42 + break 43 + 44 + if not sent: 45 + os.write(master, b'q') 46 + sent = True 47 + 48 + if exit_code is None: 49 + os.kill(pid, signal.SIGKILL) 50 + _, status = os.waitpid(pid, 0) 51 + exit_code = os.waitstatus_to_exitcode(status) 52 + 53 + attrs = termios.tcgetattr(master) 54 + lflag = attrs[3] 55 + 56 + print(f"exit={exit_code}") 57 + print(f"isig={1 if (lflag & termios.ISIG) else 0}") 58 + print(f"icanon={1 if (lflag & termios.ICANON) else 0}") 59 + print(f"echo={1 if (lflag & termios.ECHO) else 0}") 60 + `; 61 + 62 + const result = spawnSync('python3', ['-c', script, process.execPath, 'tests/amp/mini_amp_tui_repro.js'], { 63 + encoding: 'utf8', 64 + timeout: 10000, 65 + }); 66 + 67 + if (result.error && result.error.code === 'ENOENT') { 68 + console.log('skipping mini amp tty restore test because `python3` is unavailable'); 69 + process.exit(0); 70 + } 71 + 72 + if (result.error) throw result.error; 73 + 74 + const output = `${result.stdout || ''}${result.stderr || ''}`; 75 + 76 + if (!output.includes('exit=0')) { 77 + fail(`expected repro process to exit cleanly\n${output}`); 78 + } 79 + 80 + if (!output.includes('isig=1')) { 81 + fail(`expected ISIG restored after repro exit\n${output}`); 82 + } 83 + 84 + if (!output.includes('icanon=1')) { 85 + fail(`expected ICANON restored after repro exit\n${output}`); 86 + } 87 + 88 + if (!output.includes('echo=1')) { 89 + fail(`expected ECHO restored after repro exit\n${output}`); 90 + } 91 + 92 + console.log('mini amp tui repro exits and restores tty flags');