Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254// Derived from Node.js v24.16.0 (MIT; Copyright Node.js contributors, and Joyent, Inc. and// other Node contributors). See LICENSE-node.// node:tty. A process's stdio is either pipes, where isatty is false for every fd, or one// kernel pty, where fds 0-2 are the terminal: process.stdin is a tty.ReadStream (raw mode// is the ttySetRaw syscall) and process.stdout/stderr are tty.WriteStreams whose size the// kernel reports and refreshes on SIGWINCH. getColorDepth is Node's lib/internal/tty.js.//// Deviation: Node lets a guest open another ReadStream on fd 0; here process.stdin is the// only reader the kernel feeds, so a second one is refused rather than left silent.import type { SyscallSpec, WinSize } from "../shared/protocol";import type { Loop } from "./loop";import { invalidArgType } from "./errors";import { unsupported } from "./refuse";import { StdinStream } from "./stdin";import { StdioWriter } from "./stdout";import { clearLine, clearScreenDown, cursorTo, moveCursor } from "./readline/callbacks";
export interface TtyHost { loop: Loop; /** True when fds 0-2 are a pty. */ terminal: boolean; callSync<K extends keyof SyscallSpec>(op: K, args: SyscallSpec[K]["args"]): SyscallSpec[K]["ret"]; write(fd: number, bytes: Uint8Array): void; env(): Record<string, string | undefined>; emitWarning(message: string, type: string): void;}
let host: TtyHost;let stdinClaimed = false;
export function installTty(h: TtyHost): void { host = h;}
const isTerminalFd = (fd: number): boolean => host.terminal && fd >= 0 && fd <= 2;
export function isatty(fd: unknown): boolean { return Number.isInteger(fd) && (fd as number) >= 0 && (fd as number) <= 2147483647 && isTerminalFd(fd as number);}
function checkFd(fd: unknown): number { if (typeof fd !== "number" || fd >> 0 !== fd || fd < 0) { throw Object.assign(new RangeError(`"fd" must be a positive integer: ${String(fd)}`), { code: "ERR_INVALID_FD" }); } if (!isTerminalFd(fd)) { // What uv_tty_init answers for a descriptor that is not a terminal. throw Object.assign(new Error("TTY initialization failed: uv_tty_init returned EINVAL (invalid argument)"), { code: "ERR_TTY_INIT_FAILED", errno: -22, syscall: "uv_tty_init", info: { errno: -22, code: "EINVAL", message: "invalid argument", syscall: "uv_tty_init" }, }); } return fd;}
function readLoop(fd: unknown): Loop { checkFd(fd); if (stdinClaimed) throw unsupported("tty", "ReadStream", "process.stdin is the only reader of the terminal"); stdinClaimed = true; return host.loop;}
export class ReadStream extends StdinStream { isRaw = false; readonly isTTY = true;
constructor(fd: number, _options?: unknown) { // Node's tty.ReadStream reads with a high-water mark of 0. super(readLoop(fd), 0); }
setRawMode(flag: unknown): this { const raw = Boolean(flag); try { host.callSync("ttySetRaw", { raw }); } catch (e) { this.emit("error", e); return this; } this.isRaw = raw; return this; }}
export class WriteStream extends StdioWriter { columns: number; rows: number;
constructor(fd: number) { super(checkFd(fd), (bytes) => host.write(fd, bytes), (fn) => host.loop.nextTick(fn)); const size = host.callSync("ttyGetSize", {}); this.columns = size.cols; this.rows = size.rows; }
getColorDepth(env?: Record<string, string | undefined>): number { return getColorDepth(env); }
hasColors(count?: unknown, env?: Record<string, string | undefined>): boolean { return hasColors(count, env); }
_refreshSize(): void { let size: WinSize; try { size = host.callSync("ttyGetSize", {}); } catch (e) { this.emit("error", e); return; } if (this.columns !== size.cols || this.rows !== size.rows) { this.columns = size.cols; this.rows = size.rows; this.emit("resize"); } }
getWindowSize(): [number, number] { return [this.columns, this.rows]; }
cursorTo(x: unknown, y?: unknown, callback?: unknown): boolean { return cursorTo(this, x, y, callback); }
moveCursor(dx: number, dy: number, callback?: unknown): boolean { return moveCursor(this, dx, dy, callback); }
clearLine(dir: number, callback?: unknown): boolean { return clearLine(this, dir, callback); }
clearScreenDown(callback?: unknown): boolean { return clearScreenDown(this, callback); }}Object.defineProperty(WriteStream.prototype, "isTTY", { value: true, writable: true, configurable: true });
// ---- colour depth (lib/internal/tty.js) ----
const COLORS_2 = 1;const COLORS_16 = 4;const COLORS_256 = 8;const COLORS_16m = 24;
const TERM_ENVS: Record<string, number> = { eterm: COLORS_16, cons25: COLORS_16, console: COLORS_16, cygwin: COLORS_16, dtterm: COLORS_16, gnome: COLORS_16, hurd: COLORS_16, jfbterm: COLORS_16, konsole: COLORS_16, kterm: COLORS_16, mlterm: COLORS_16, mosh: COLORS_16m, putty: COLORS_16, st: COLORS_16, "rxvt-unicode-24bit": COLORS_16m, terminator: COLORS_16m, "xterm-kitty": COLORS_16m,};
const CI_ENVS: Array<[string, number]> = [ ["APPVEYOR", COLORS_256], ["BUILDKITE", COLORS_256], ["CIRCLECI", COLORS_16m], ["DRONE", COLORS_256], ["GITEA_ACTIONS", COLORS_16m], ["GITHUB_ACTIONS", COLORS_16m], ["GITLAB_CI", COLORS_256], ["TRAVIS", COLORS_256],];
const TERM_ENVS_REG_EXP = [/ansi/, /color/, /linux/, /direct/, /^con[0-9]*x[0-9]/, /^rxvt/, /^screen/, /^xterm/, /^vt100/, /^vt220/];
let warned = false;function warnOnDeactivatedColors(env: Record<string, string | undefined>): void { if (warned) return; let name = ""; if (env.NODE_DISABLE_COLORS !== undefined && env.NODE_DISABLE_COLORS !== "") name = "NODE_DISABLE_COLORS"; if (env.NO_COLOR !== undefined && env.NO_COLOR !== "") { if (name !== "") name += "' and '"; name += "NO_COLOR"; } if (name !== "") { host.emitWarning(`The '${name}' env is ignored due to the 'FORCE_COLOR' env being set.`, "Warning"); warned = true; }}
export function getColorDepth(env: Record<string, string | undefined> = host.env()): number { if (env.FORCE_COLOR !== undefined) { switch (env.FORCE_COLOR) { case "": case "1": case "true": warnOnDeactivatedColors(env); return COLORS_16; case "2": warnOnDeactivatedColors(env); return COLORS_256; case "3": warnOnDeactivatedColors(env); return COLORS_16m; default: return COLORS_2; } } if ((env.NODE_DISABLE_COLORS !== undefined && env.NODE_DISABLE_COLORS !== "") || (env.NO_COLOR !== undefined && env.NO_COLOR !== "") || env.TERM === "dumb") { return COLORS_2; } // process.platform is linux, so the win32 branch never applies. if (env.TMUX) return COLORS_16m; if (Object.hasOwn(env, "TF_BUILD") && Object.hasOwn(env, "AGENT_NAME")) return COLORS_16; if (Object.hasOwn(env, "CI")) { for (const [name, colors] of CI_ENVS) if (Object.hasOwn(env, name)) return colors; if (env.CI_NAME === "codeship") return COLORS_256; return COLORS_2; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.exec(env.TEAMCITY_VERSION ?? "") !== null ? COLORS_16 : COLORS_2; } switch (env.TERM_PROGRAM) { case "iTerm.app": if (!env.TERM_PROGRAM_VERSION || /^[0-2]\./.exec(env.TERM_PROGRAM_VERSION) !== null) return COLORS_256; return COLORS_16m; case "HyperTerm": case "MacTerm": return COLORS_16m; case "Apple_Terminal": return COLORS_256; } if (env.COLORTERM === "truecolor" || env.COLORTERM === "24bit") return COLORS_16m; if (env.TERM) { if (/truecolor/.exec(env.TERM) !== null) return COLORS_16m; if (/^xterm-256/.exec(env.TERM) !== null) return COLORS_256; const termEnv = env.TERM.toLowerCase(); if (TERM_ENVS[termEnv]) return TERM_ENVS[termEnv]!; if (TERM_ENVS_REG_EXP.some((term) => term.exec(termEnv) !== null)) return COLORS_16; } if (env.COLORTERM) return COLORS_16; return COLORS_2;}
export function hasColors(count?: unknown, env?: Record<string, string | undefined>): boolean { if (env === undefined && (count === undefined || (typeof count === "object" && count !== null))) { env = count as Record<string, string | undefined> | undefined; count = 16; } else if (typeof count !== "number") { throw invalidArgType("count", "of type number", count); } else if (!Number.isInteger(count) || count < 2 || count > Number.MAX_SAFE_INTEGER) { const must = Number.isInteger(count) ? ">= 2 && <= 9007199254740991" : "an integer"; throw Object.assign(new RangeError(`The value of "count" is out of range. It must be ${must}. Received ${count}`), { code: "ERR_OUT_OF_RANGE", }); } return (count as number) <= 2 ** getColorDepth(env);}
/** Node's shouldColorize (lib/internal/util/colors.js): what console and styleText use. */export function shouldColorize(stream: unknown): boolean { if (host.env().FORCE_COLOR !== undefined) return getColorDepth() > 2; const s = stream as { isTTY?: boolean; getColorDepth?: () => number } | null | undefined; return Boolean(s?.isTTY && (typeof s.getColorDepth === "function" ? s.getColorDepth() > 2 : true));}
export const tty = { isatty, ReadStream, WriteStream };