ktx/packages/cli/src/viz-fallback.ts

94 lines
2.3 KiB
TypeScript
Raw Permalink Normal View History

2026-05-10 23:12:26 +02:00
import { profileMark } from './startup-profile.js';
profileMark('module:viz-fallback');
2026-05-10 23:51:24 +02:00
type KtxVizFallbackReason =
2026-05-10 23:12:26 +02:00
| 'stdout-not-tty'
| 'term-dumb'
| 'stdin-not-tty'
| 'stdin-raw-mode-unavailable'
| 'renderer-unavailable';
2026-05-10 23:51:24 +02:00
interface KtxVizFallbackIo {
2026-05-10 23:12:26 +02:00
stdin?: { isTTY?: boolean; setRawMode?(value: boolean): void };
stdout: { isTTY?: boolean };
stderr: { write(chunk: string): void };
}
2026-05-10 23:51:24 +02:00
interface KtxVizFallbackOptions {
2026-05-10 23:12:26 +02:00
requireInput?: boolean;
}
2026-05-10 23:51:24 +02:00
type KtxVizFallbackDecision =
2026-05-10 23:12:26 +02:00
| {
shouldDegrade: false;
}
| {
shouldDegrade: true;
2026-05-10 23:51:24 +02:00
reason: KtxVizFallbackReason;
2026-05-10 23:12:26 +02:00
message: string;
};
2026-05-10 23:51:24 +02:00
const warnedFallbackReasons = new Set<KtxVizFallbackReason>();
2026-05-10 23:12:26 +02:00
export function resolveVizFallback(
2026-05-10 23:51:24 +02:00
io: KtxVizFallbackIo,
2026-05-10 23:12:26 +02:00
env: NodeJS.ProcessEnv = process.env,
2026-05-10 23:51:24 +02:00
options: KtxVizFallbackOptions = {},
): KtxVizFallbackDecision {
2026-05-10 23:12:26 +02:00
if (io.stdout.isTTY !== true) {
return {
shouldDegrade: true,
reason: 'stdout-not-tty',
message: 'stdout is not an interactive terminal',
};
}
if ((env.TERM ?? '').toLowerCase() === 'dumb') {
return {
shouldDegrade: true,
reason: 'term-dumb',
message: 'TERM=dumb does not support the visual renderer',
};
}
if (options.requireInput === true && io.stdin?.isTTY !== true) {
return {
shouldDegrade: true,
reason: 'stdin-not-tty',
message: 'stdin is not an interactive terminal',
};
}
if (options.requireInput === true && typeof io.stdin?.setRawMode !== 'function') {
return {
shouldDegrade: true,
reason: 'stdin-raw-mode-unavailable',
message: 'stdin raw mode is unavailable',
};
}
return { shouldDegrade: false };
}
2026-05-10 23:51:24 +02:00
export function rendererUnavailableVizFallback(): KtxVizFallbackDecision {
2026-05-10 23:12:26 +02:00
return {
shouldDegrade: true,
reason: 'renderer-unavailable',
message: 'the terminal renderer is unavailable',
};
}
2026-05-10 23:51:24 +02:00
export function warnVizFallbackOnce(io: KtxVizFallbackIo, decision: KtxVizFallbackDecision): void {
2026-05-10 23:12:26 +02:00
if (!decision.shouldDegrade || warnedFallbackReasons.has(decision.reason)) {
return;
}
warnedFallbackReasons.add(decision.reason);
io.stderr.write(`Visualization requested but ${decision.message}; printing plain output.\n`);
}
export function resetVizFallbackWarningsForTest(): void {
warnedFallbackReasons.clear();
}