treewide: adapt for monorepo layout; initial TUI work

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Id40b5f5ccb55a8a1ea2793192a38f0256a6a6964
This commit is contained in:
raf 2026-02-07 12:43:59 +03:00
commit 33ec901788
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
35 changed files with 1699 additions and 413 deletions

4
packages/tui/README.md Normal file
View file

@ -0,0 +1,4 @@
# @ns/tui
Provides a terminal-based interface for viewing and analyzing Nix evaluator
statistics.

20
packages/tui/package.json Normal file
View file

@ -0,0 +1,20 @@
{
"name": "@ns/tui",
"version": "1.0.0",
"type": "module",
"bin": {
"ns-tui": "./dist/cli.js"
},
"scripts": {
"build": "tsc",
"check": "tsc --noEmit",
"dev": "node --loader ts-node/esm src/cli.ts"
},
"dependencies": {
"@ns/core": "workspace:*"
},
"devDependencies": {
"@types/node": "^25.0.10",
"typescript": "^5.9.3"
}
}

60
packages/tui/src/cli.ts Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env node
import { readFile } from 'fs/promises';
import { parseStats } from '@ns/core';
async function main() {
const args = process.argv.slice(2);
// FIXME: nuke all of this actually
if (args.length === 0) {
console.log('NS');
console.log('\nUsage: ns-tui <stats.json>');
process.exit(1);
}
const filePath = args[0];
try {
const content = await readFile(filePath, 'utf-8');
const raw = JSON.parse(content);
const stats = parseStats(raw);
console.log('\n=== Nix Evaluator Statistics ===\n');
console.log(`CPU Time: ${stats.cpuTime.toFixed(3)}s`);
console.log(`Expressions: ${stats.nrExprs.toLocaleString()}`);
console.log(`Thunks: ${stats.nrThunks.toLocaleString()}`);
console.log(` - Avoided: ${stats.nrAvoided.toLocaleString()}`);
console.log(` - Ratio: ${((stats.nrAvoided / stats.nrThunks) * 100).toFixed(2)}%`);
const totalMemory =
stats.envs.bytes +
stats.list.bytes +
stats.values.bytes +
stats.symbols.bytes +
stats.sets.bytes;
console.log(`Total Memory: ${(totalMemory / 1024 / 1024).toFixed(2)} MB`);
console.log('\n=== Memory Breakdown ===\n');
console.log(`Environments: ${(stats.envs.bytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`Lists: ${(stats.list.bytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`Values: ${(stats.values.bytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`Symbols: ${(stats.symbols.bytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`Sets: ${(stats.sets.bytes / 1024 / 1024).toFixed(2)} MB`);
if (stats.gc) {
console.log('\n=== Garbage Collection ===\n');
console.log(`Heap Size: ${(stats.gc.heapSize / 1024 / 1024).toFixed(2)} MB`);
console.log(`Total Alloc: ${(stats.gc.totalBytes / 1024 / 1024).toFixed(2)} MB`);
console.log(`GC Cycles: ${stats.gc.cycles.toLocaleString()}`);
}
console.log('\n' + '='.repeat(40));
console.log('='.repeat(40) + '\n');
} catch (error) {
console.error('Error:', error instanceof Error ? error.message : String(error));
process.exit(1);
}
}
main();

View file

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"lib": ["ES2022"],
"moduleResolution": "bundler",
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}