scripter-x 1.0.33 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/update.js +51 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scripter-x",
3
- "version": "1.0.33",
3
+ "version": "1.0.34",
4
4
  "description": "ScripterX — local Flipkart session extractor (runs on your residential IP, syncs to marthunt)",
5
5
  "type": "module",
6
6
  "bin": {
package/src/update.js CHANGED
@@ -48,44 +48,86 @@ export async function runUpdate() {
48
48
  }
49
49
  }
50
50
 
51
+ // Hard-reset the terminal so the freshly-spawned child inherits a CLEAN, cooked TTY.
52
+ //
53
+ // THE BUG this fixes: after the old ink app unmounts, the terminal could still be left in
54
+ // raw mode WITH SGR mouse tracking enabled (`\x1b[?1000h…`). When the restarted process
55
+ // took over that same TTY, every keypress/click emitted raw escape/coordinate codes that
56
+ // got echoed as garbage ("random shit on every keypress") until the user killed + relaunched
57
+ // manually. ink's alt-screen restore only ran on `process.on('exit')` AND the old + new
58
+ // processes briefly shared the TTY, so the disable sequences raced the child's re-enable.
59
+ //
60
+ // Fix: synchronously (1) disable ALL mouse modes + restore the screen, (2) take stdin out of
61
+ // raw mode, pause it, and drop every listener — BEFORE the child is spawned — so there is no
62
+ // window where mouse reporting or a stray raw-mode listener can corrupt the child's input.
63
+ function resetTerminal() {
64
+ try {
65
+ if (process.stdout.isTTY) {
66
+ // disable button/motion/SGR mouse modes (the source of the coordinate-code garbage)
67
+ process.stdout.write('\x1b[?1000l\x1b[?1003l\x1b[?1006l');
68
+ process.stdout.write('\x1b[?25h'); // show cursor
69
+ process.stdout.write('\x1b[?1049l'); // leave alt-screen → normal buffer
70
+ }
71
+ } catch { /* */ }
72
+ try {
73
+ if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') {
74
+ process.stdin.setRawMode(false); // back to cooked/canonical mode
75
+ }
76
+ // drop every data/keypress listener the old ink app (or our mouse hook) attached, then
77
+ // pause so nothing in THIS process consumes input meant for the child.
78
+ process.stdin.removeAllListeners('data');
79
+ process.stdin.removeAllListeners('keypress');
80
+ process.stdin.pause();
81
+ } catch { /* */ }
82
+ }
83
+
51
84
  // Re-exec the CLI so the just-installed version takes over. Spawns a fresh, DETACHED
52
85
  // process that inherits this terminal, then exits the current one. The caller MUST have
53
- // already torn down the ink/alt-screen UI (so the terminal is restored) before calling
54
- // this — otherwise the child renders over a dirty screen.
86
+ // already torn down the ink/alt-screen UI before calling this.
55
87
  //
56
88
  // We re-run the CURRENT entry file with node (`process.argv[1]`): after `npm install -g`,
57
89
  // the global bin symlink points at the new version, so this loads the updated code. If
58
90
  // that entry path is somehow missing, we fall back to the `scripterx` bin on PATH.
59
91
  //
92
+ // ORDERING is what makes the restart clean: we resetTerminal() FIRST (cooked TTY, mouse off,
93
+ // stdin drained), THEN spawn the child, THEN exit immediately on the next tick. The child
94
+ // only sets up raw mode / mouse tracking after it boots — by which point the parent is gone,
95
+ // so the two never fight over the TTY. No more leaked keystrokes.
96
+ //
60
97
  // NOTE: on success it exits the process and never returns. Returns false only if BOTH
61
98
  // spawn attempts throw synchronously, so the caller can show "restart manually".
62
- export function restartCli({ binName = 'scripterx', delayMs = 150 } = {}) {
99
+ export function restartCli({ binName = 'scripterx' } = {}) {
63
100
  // Re-run with the SAME args the user launched with (drop node + the script path), so
64
101
  // `scripterx` → interactive shell, `scripterx zepto` → zepto, etc.
65
102
  const argv = process.argv.slice(2);
66
103
  const entry = process.argv[1];
67
104
 
105
+ // Clean the terminal BEFORE spawning so the child never inherits raw mode / mouse mode.
106
+ resetTerminal();
107
+
68
108
  const trySpawn = (cmd, args) => {
69
109
  const child = spawn(cmd, args, { stdio: 'inherit', detached: true, shell: false });
70
110
  child.unref();
71
111
  return child;
72
112
  };
73
113
 
114
+ const exitSoon = () => {
115
+ // Exit on the next tick (not after a long timer) so we don't linger on the TTY. The
116
+ // detached child has already inherited the (now clean) fds and takes over.
117
+ setImmediate(() => process.exit(0));
118
+ };
119
+
74
120
  try {
75
121
  const primary = trySpawn(process.execPath, [entry, ...argv]);
76
- // If the entry re-exec fails to even start, fall back to the named bin on PATH.
77
122
  primary.on('error', () => {
78
123
  try { trySpawn(binName, argv); } catch { /* nothing more we can do */ }
79
124
  });
80
- // Exit the old process; its `process.on('exit', restore)` cleans the terminal and the
81
- // detached child takes over the same TTY running the new version.
82
- setTimeout(() => process.exit(0), delayMs);
125
+ exitSoon();
83
126
  return true;
84
127
  } catch {
85
- // process.execPath spawn threw synchronously — try the named bin directly.
86
128
  try {
87
129
  trySpawn(binName, argv);
88
- setTimeout(() => process.exit(0), delayMs);
130
+ exitSoon();
89
131
  return true;
90
132
  } catch {
91
133
  return false;