mirror of
https://github.com/NotAShelf/nix-evaluator-stats.git
synced 2026-04-12 14:27:41 +00:00
treewide: adapt for monorepo layout; initial TUI work
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Id40b5f5ccb55a8a1ea2793192a38f0256a6a6964
This commit is contained in:
parent
e36b0d89da
commit
33ec901788
35 changed files with 1699 additions and 413 deletions
27
packages/core/README.md
Normal file
27
packages/core/README.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# @ns/core
|
||||
|
||||
Core types and parsing logic for Nix evaluator statistics. This package is
|
||||
framework-agnostic and can be used in any JavaScript/TypeScript environment.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { calculateChange, parseStats, StatsData } from "@ns/core";
|
||||
|
||||
// Parse raw stats from Nix
|
||||
const raw = JSON.parse(statsJson);
|
||||
const stats: StatsData = parseStats(raw);
|
||||
|
||||
console.log(`CPU Time: ${stats.cpuTime}s`);
|
||||
console.log(`Expressions: ${stats.nrExprs}`);
|
||||
|
||||
// Compare two values
|
||||
const change = calculateChange(stats.nrThunks, previousStats.nrThunks);
|
||||
console.log(`Thunks changed by ${change.percent.toFixed(2)}%`);
|
||||
```
|
||||
|
||||
## Version Compatibility
|
||||
|
||||
The parser handles different Nix implementations (Nix, Lix, Snix, etc.) by
|
||||
checking for field existence in the raw JSON, since not all implementations
|
||||
expose the same statistics.
|
||||
20
packages/core/package.json
Normal file
20
packages/core/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "@ns/core",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
2
packages/core/src/index.ts
Normal file
2
packages/core/src/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './types';
|
||||
export * from './parser';
|
||||
105
packages/core/src/parser.ts
Normal file
105
packages/core/src/parser.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { StatsData } from './types';
|
||||
|
||||
const num = (val: unknown): number => (typeof val === 'number' ? val : 0);
|
||||
|
||||
export function parseStats(json: Record<string, unknown>): StatsData {
|
||||
const timeObj = json.time as Record<string, unknown> | undefined;
|
||||
const envsObj = json.envs as Record<string, unknown> | undefined;
|
||||
const listObj = json.list as Record<string, unknown> | undefined;
|
||||
const valuesObj = json.values as Record<string, unknown> | undefined;
|
||||
const symbolsObj = json.symbols as Record<string, unknown> | undefined;
|
||||
const setsObj = json.sets as Record<string, unknown> | undefined;
|
||||
const sizesObj = json.sizes as Record<string, unknown> | undefined;
|
||||
|
||||
const stats: StatsData = {
|
||||
cpuTime: num(json.cpuTime),
|
||||
time: {
|
||||
cpu: num(timeObj?.cpu) || num(json.cpuTime),
|
||||
gc: num(timeObj?.gc),
|
||||
gcNonIncremental: num(timeObj?.gcNonIncremental),
|
||||
gcFraction: num(timeObj?.gcFraction),
|
||||
gcNonIncrementalFraction: num(timeObj?.gcNonIncrementalFraction),
|
||||
},
|
||||
envs: {
|
||||
number: num(envsObj?.number),
|
||||
elements: num(envsObj?.elements),
|
||||
bytes: num(envsObj?.bytes),
|
||||
},
|
||||
list: {
|
||||
elements: num(listObj?.elements),
|
||||
bytes: num(listObj?.bytes),
|
||||
concats: num(listObj?.concats),
|
||||
},
|
||||
values: { number: num(valuesObj?.number), bytes: num(valuesObj?.bytes) },
|
||||
symbols: { number: num(symbolsObj?.number), bytes: num(symbolsObj?.bytes) },
|
||||
sets: {
|
||||
number: num(setsObj?.number),
|
||||
elements: num(setsObj?.elements),
|
||||
bytes: num(setsObj?.bytes),
|
||||
},
|
||||
sizes: {
|
||||
Env: num(sizesObj?.Env),
|
||||
Value: num(sizesObj?.Value),
|
||||
Bindings: num(sizesObj?.Bindings),
|
||||
Attr: num(sizesObj?.Attr),
|
||||
},
|
||||
nrExprs: num(json.nrExprs),
|
||||
nrThunks: num(json.nrThunks),
|
||||
nrAvoided: num(json.nrAvoided),
|
||||
nrLookups: num(json.nrLookups),
|
||||
nrOpUpdates: num(json.nrOpUpdates),
|
||||
nrOpUpdateValuesCopied: num(json.nrOpUpdateValuesCopied),
|
||||
nrPrimOpCalls: num(json.nrPrimOpCalls),
|
||||
nrFunctionCalls: num(json.nrFunctionCalls),
|
||||
};
|
||||
|
||||
if (json.gc && typeof json.gc === 'object') {
|
||||
const gc = json.gc as Record<string, unknown>;
|
||||
stats.gc = {
|
||||
heapSize: num(gc.heapSize),
|
||||
totalBytes: num(gc.totalBytes),
|
||||
cycles: num(gc.cycles),
|
||||
};
|
||||
}
|
||||
|
||||
if (json.primops && typeof json.primops === 'object')
|
||||
stats.primops = json.primops as Record<string, number>;
|
||||
if (json.functions && Array.isArray(json.functions))
|
||||
stats.functions = json.functions as StatsData['functions'];
|
||||
if (json.attributes && Array.isArray(json.attributes))
|
||||
stats.attributes = json.attributes as StatsData['attributes'];
|
||||
|
||||
const storeFields = [
|
||||
'narInfoRead',
|
||||
'narInfoReadAverted',
|
||||
'narInfoMissing',
|
||||
'narInfoWrite',
|
||||
'narRead',
|
||||
'narReadBytes',
|
||||
'narReadCompressedBytes',
|
||||
'narWrite',
|
||||
'narWriteAverted',
|
||||
'narWriteBytes',
|
||||
'narWriteCompressedBytes',
|
||||
] as const;
|
||||
|
||||
for (const field of storeFields) {
|
||||
if (typeof json[field] === 'number')
|
||||
(stats as unknown as Record<string, number>)[field] = json[field] as number;
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
export function calculateChange(
|
||||
current: number,
|
||||
previous: number,
|
||||
): { value: number; percent: number; isReduction: boolean } {
|
||||
if (previous === 0) {
|
||||
const percent = current === 0 ? 0 : 100;
|
||||
return { value: current, percent, isReduction: false };
|
||||
}
|
||||
const value = current - previous;
|
||||
const percent = (value / previous) * 100;
|
||||
return { value, percent, isReduction: value < 0 };
|
||||
}
|
||||
85
packages/core/src/types.ts
Normal file
85
packages/core/src/types.ts
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
export interface StatsData {
|
||||
cpuTime: number;
|
||||
time: {
|
||||
cpu: number;
|
||||
gc?: number;
|
||||
gcNonIncremental?: number;
|
||||
gcFraction?: number;
|
||||
gcNonIncrementalFraction?: number;
|
||||
};
|
||||
envs: {
|
||||
number: number;
|
||||
elements: number;
|
||||
bytes: number;
|
||||
};
|
||||
list: {
|
||||
elements: number;
|
||||
bytes: number;
|
||||
concats: number;
|
||||
};
|
||||
values: {
|
||||
number: number;
|
||||
bytes: number;
|
||||
};
|
||||
symbols: {
|
||||
number: number;
|
||||
bytes: number;
|
||||
};
|
||||
sets: {
|
||||
number: number;
|
||||
elements: number;
|
||||
bytes: number;
|
||||
};
|
||||
sizes: {
|
||||
Env: number;
|
||||
Value: number;
|
||||
Bindings: number;
|
||||
Attr: number;
|
||||
};
|
||||
nrExprs: number;
|
||||
nrThunks: number;
|
||||
nrAvoided: number;
|
||||
nrLookups: number;
|
||||
nrOpUpdates: number;
|
||||
nrOpUpdateValuesCopied: number;
|
||||
nrPrimOpCalls: number;
|
||||
nrFunctionCalls: number;
|
||||
gc?: {
|
||||
heapSize: number;
|
||||
totalBytes: number;
|
||||
cycles: number;
|
||||
};
|
||||
primops?: Record<string, number>;
|
||||
functions?: Array<{
|
||||
name: string | null;
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
count: number;
|
||||
}>;
|
||||
attributes?: Array<{
|
||||
file: string;
|
||||
line: number;
|
||||
column: number;
|
||||
count: number;
|
||||
}>;
|
||||
narInfoRead?: number;
|
||||
narInfoReadAverted?: number;
|
||||
narInfoMissing?: number;
|
||||
narInfoWrite?: number;
|
||||
narRead?: number;
|
||||
narReadBytes?: number;
|
||||
narReadCompressedBytes?: number;
|
||||
narWrite?: number;
|
||||
narWriteAverted?: number;
|
||||
narWriteBytes?: number;
|
||||
narWriteCompressedBytes?: number;
|
||||
}
|
||||
|
||||
export interface ComparisonEntry {
|
||||
id: number;
|
||||
name: string;
|
||||
data: StatsData;
|
||||
raw: Record<string, unknown>;
|
||||
timestamp: Date;
|
||||
}
|
||||
18
packages/core/tsconfig.json
Normal file
18
packages/core/tsconfig.json
Normal 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/**/*"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue