Files
vue-tui/packages
Yunfei He 19c475ae33 fix(runtime): propagate thrown-undefined + non-Error messages from renderToString; don't leak stdin.ref on setRawMode throw (#203)
Three small confirmed fixes:

- renderToString swallowed a component that threw literal `undefined`: the
  `uncaughtError !== undefined` sentinel could not tell "threw undefined" from
  "no error", so it returned the normal frame instead of propagating. Track
  occurrence with a separate `errored` boolean (mirrors the live renderer's
  onErrorCaptured `errored` flag).

- renderToString wrapped a non-Error throw as `new Error(String(value))`, so
  `{ message: "detail" }` became "[object Object]". Re-throw a genuine Error
  (incl. cross-realm, via the `[object Error]` brand check) as-is, and wrap a
  true non-Error with `messageForNonError` so its message survives. Relocated
  `isErrorInput` from render.ts into error-overview.ts (next to
  messageForNonError) so both renderers share one source of truth.

- acquireRawMode called `stdin.ref()` before `setRawMode(true)`. On a hostile
  PTY setRawMode throws ERR_TTY_INIT_FAILED after the ref but before the refcount
  increments, so dispose's gated unref never ran and the ref'd stdin kept the
  event loop alive. Reorder setRawMode(true) before stdin.ref() so a throw
  leaves nothing ref'd.

Test-first: added reproducing tests for each (renderToString throw-undefined and
non-Error-message in render-to-string.test.tsx; ref/unref balance on a throwing
setRawMode in raw-mode-ref-leak.sequential.test.tsx), confirmed red, then green.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:17:05 +08:00
..