mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 17:46:58 +10:00
220 lines
22 KiB
TypeScript
220 lines
22 KiB
TypeScript
|
|
import { c as _c } from "react/compiler-runtime";
|
||
|
|
import React, { createContext, useCallback, useContext, useEffect, useMemo } from 'react';
|
||
|
|
import { saveCurrentProjectConfig } from '../utils/config.js';
|
||
|
|
export type StatsStore = {
|
||
|
|
increment(name: string, value?: number): void;
|
||
|
|
set(name: string, value: number): void;
|
||
|
|
observe(name: string, value: number): void;
|
||
|
|
add(name: string, value: string): void;
|
||
|
|
getAll(): Record<string, number>;
|
||
|
|
};
|
||
|
|
function percentile(sorted: number[], p: number): number {
|
||
|
|
const index = p / 100 * (sorted.length - 1);
|
||
|
|
const lower = Math.floor(index);
|
||
|
|
const upper = Math.ceil(index);
|
||
|
|
if (lower === upper) {
|
||
|
|
return sorted[lower]!;
|
||
|
|
}
|
||
|
|
return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (index - lower);
|
||
|
|
}
|
||
|
|
const RESERVOIR_SIZE = 1024;
|
||
|
|
type Histogram = {
|
||
|
|
reservoir: number[];
|
||
|
|
count: number;
|
||
|
|
sum: number;
|
||
|
|
min: number;
|
||
|
|
max: number;
|
||
|
|
};
|
||
|
|
export function createStatsStore(): StatsStore {
|
||
|
|
const metrics = new Map<string, number>();
|
||
|
|
const histograms = new Map<string, Histogram>();
|
||
|
|
const sets = new Map<string, Set<string>>();
|
||
|
|
return {
|
||
|
|
increment(name: string, value = 1) {
|
||
|
|
metrics.set(name, (metrics.get(name) ?? 0) + value);
|
||
|
|
},
|
||
|
|
set(name: string, value: number) {
|
||
|
|
metrics.set(name, value);
|
||
|
|
},
|
||
|
|
observe(name: string, value: number) {
|
||
|
|
let h = histograms.get(name);
|
||
|
|
if (!h) {
|
||
|
|
h = {
|
||
|
|
reservoir: [],
|
||
|
|
count: 0,
|
||
|
|
sum: 0,
|
||
|
|
min: value,
|
||
|
|
max: value
|
||
|
|
};
|
||
|
|
histograms.set(name, h);
|
||
|
|
}
|
||
|
|
h.count++;
|
||
|
|
h.sum += value;
|
||
|
|
if (value < h.min) {
|
||
|
|
h.min = value;
|
||
|
|
}
|
||
|
|
if (value > h.max) {
|
||
|
|
h.max = value;
|
||
|
|
}
|
||
|
|
// Reservoir sampling (Algorithm R)
|
||
|
|
if (h.reservoir.length < RESERVOIR_SIZE) {
|
||
|
|
h.reservoir.push(value);
|
||
|
|
} else {
|
||
|
|
const j = Math.floor(Math.random() * h.count);
|
||
|
|
if (j < RESERVOIR_SIZE) {
|
||
|
|
h.reservoir[j] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
add(name: string, value: string) {
|
||
|
|
let s = sets.get(name);
|
||
|
|
if (!s) {
|
||
|
|
s = new Set();
|
||
|
|
sets.set(name, s);
|
||
|
|
}
|
||
|
|
s.add(value);
|
||
|
|
},
|
||
|
|
getAll() {
|
||
|
|
const result: Record<string, number> = Object.fromEntries(metrics);
|
||
|
|
for (const [name, h] of histograms) {
|
||
|
|
if (h.count === 0) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
result[`${name}_count`] = h.count;
|
||
|
|
result[`${name}_min`] = h.min;
|
||
|
|
result[`${name}_max`] = h.max;
|
||
|
|
result[`${name}_avg`] = h.sum / h.count;
|
||
|
|
const sorted = [...h.reservoir].sort((a, b) => a - b);
|
||
|
|
result[`${name}_p50`] = percentile(sorted, 50);
|
||
|
|
result[`${name}_p95`] = percentile(sorted, 95);
|
||
|
|
result[`${name}_p99`] = percentile(sorted, 99);
|
||
|
|
}
|
||
|
|
for (const [name, s] of sets) {
|
||
|
|
result[name] = s.size;
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
export const StatsContext = createContext<StatsStore | null>(null);
|
||
|
|
type Props = {
|
||
|
|
store?: StatsStore;
|
||
|
|
children: React.ReactNode;
|
||
|
|
};
|
||
|
|
export function StatsProvider(t0) {
|
||
|
|
const $ = _c(7);
|
||
|
|
const {
|
||
|
|
store: externalStore,
|
||
|
|
children
|
||
|
|
} = t0;
|
||
|
|
let t1;
|
||
|
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = createStatsStore();
|
||
|
|
$[0] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[0];
|
||
|
|
}
|
||
|
|
const internalStore = t1;
|
||
|
|
const store = externalStore ?? internalStore;
|
||
|
|
let t2;
|
||
|
|
let t3;
|
||
|
|
if ($[1] !== store) {
|
||
|
|
t2 = () => {
|
||
|
|
const flush = () => {
|
||
|
|
const metrics = store.getAll();
|
||
|
|
if (Object.keys(metrics).length > 0) {
|
||
|
|
saveCurrentProjectConfig(current => ({
|
||
|
|
...current,
|
||
|
|
lastSessionMetrics: metrics
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
process.on("exit", flush);
|
||
|
|
return () => {
|
||
|
|
process.off("exit", flush);
|
||
|
|
};
|
||
|
|
};
|
||
|
|
t3 = [store];
|
||
|
|
$[1] = store;
|
||
|
|
$[2] = t2;
|
||
|
|
$[3] = t3;
|
||
|
|
} else {
|
||
|
|
t2 = $[2];
|
||
|
|
t3 = $[3];
|
||
|
|
}
|
||
|
|
useEffect(t2, t3);
|
||
|
|
let t4;
|
||
|
|
if ($[4] !== children || $[5] !== store) {
|
||
|
|
t4 = <StatsContext.Provider value={store}>{children}</StatsContext.Provider>;
|
||
|
|
$[4] = children;
|
||
|
|
$[5] = store;
|
||
|
|
$[6] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[6];
|
||
|
|
}
|
||
|
|
return t4;
|
||
|
|
}
|
||
|
|
export function useStats() {
|
||
|
|
const store = useContext(StatsContext);
|
||
|
|
if (!store) {
|
||
|
|
throw new Error("useStats must be used within a StatsProvider");
|
||
|
|
}
|
||
|
|
return store;
|
||
|
|
}
|
||
|
|
export function useCounter(name) {
|
||
|
|
const $ = _c(3);
|
||
|
|
const store = useStats();
|
||
|
|
let t0;
|
||
|
|
if ($[0] !== name || $[1] !== store) {
|
||
|
|
t0 = value => store.increment(name, value);
|
||
|
|
$[0] = name;
|
||
|
|
$[1] = store;
|
||
|
|
$[2] = t0;
|
||
|
|
} else {
|
||
|
|
t0 = $[2];
|
||
|
|
}
|
||
|
|
return t0;
|
||
|
|
}
|
||
|
|
export function useGauge(name) {
|
||
|
|
const $ = _c(3);
|
||
|
|
const store = useStats();
|
||
|
|
let t0;
|
||
|
|
if ($[0] !== name || $[1] !== store) {
|
||
|
|
t0 = value => store.set(name, value);
|
||
|
|
$[0] = name;
|
||
|
|
$[1] = store;
|
||
|
|
$[2] = t0;
|
||
|
|
} else {
|
||
|
|
t0 = $[2];
|
||
|
|
}
|
||
|
|
return t0;
|
||
|
|
}
|
||
|
|
export function useTimer(name) {
|
||
|
|
const $ = _c(3);
|
||
|
|
const store = useStats();
|
||
|
|
let t0;
|
||
|
|
if ($[0] !== name || $[1] !== store) {
|
||
|
|
t0 = value => store.observe(name, value);
|
||
|
|
$[0] = name;
|
||
|
|
$[1] = store;
|
||
|
|
$[2] = t0;
|
||
|
|
} else {
|
||
|
|
t0 = $[2];
|
||
|
|
}
|
||
|
|
return t0;
|
||
|
|
}
|
||
|
|
export function useSet(name) {
|
||
|
|
const $ = _c(3);
|
||
|
|
const store = useStats();
|
||
|
|
let t0;
|
||
|
|
if ($[0] !== name || $[1] !== store) {
|
||
|
|
t0 = value => store.add(name, value);
|
||
|
|
$[0] = name;
|
||
|
|
$[1] = store;
|
||
|
|
$[2] = t0;
|
||
|
|
} else {
|
||
|
|
t0 = $[2];
|
||
|
|
}
|
||
|
|
return t0;
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJ1c2VDYWxsYmFjayIsInVzZUNvbnRleHQiLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwic2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnIiwiU3RhdHNTdG9yZSIsImluY3JlbWVudCIsIm5hbWUiLCJ2YWx1ZSIsInNldCIsIm9ic2VydmUiLCJhZGQiLCJnZXRBbGwiLCJSZWNvcmQiLCJwZXJjZW50aWxlIiwic29ydGVkIiwicCIsImluZGV4IiwibGVuZ3RoIiwibG93ZXIiLCJNYXRoIiwiZmxvb3IiLCJ1cHBlciIsImNlaWwiLCJSRVNFUlZPSVJfU0laRSIsIkhpc3RvZ3JhbSIsInJlc2Vydm9pciIsImNvdW50Iiwic3VtIiwibWluIiwibWF4IiwiY3JlYXRlU3RhdHNTdG9yZSIsIm1ldHJpY3MiLCJNYXAiLCJoaXN0b2dyYW1zIiwic2V0cyIsIlNldCIsImdldCIsImgiLCJwdXNoIiwiaiIsInJhbmRvbSIsInMiLCJyZXN1bHQiLCJPYmplY3QiLCJmcm9tRW50cmllcyIsInNvcnQiLCJhIiwiYiIsInNpemUiLCJTdGF0c0NvbnRleHQiLCJQcm9wcyIsInN0b3JlIiwiY2hpbGRyZW4iLCJSZWFjdE5vZGUiLCJTdGF0c1Byb3ZpZGVyIiwidDAiLCIkIiwiX2MiLCJleHRlcm5hbFN0b3JlIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJpbnRlcm5hbFN0b3JlIiwidDIiLCJ0MyIsImZsdXNoIiwia2V5cyIsImN1cnJlbnQiLCJsYXN0U2Vzc2lvbk1ldHJpY3MiLCJwcm9jZXNzIiwib24iLCJvZmYiLCJ0NCIsInVzZVN0YXRzIiwiRXJyb3IiLCJ1c2VDb3VudGVyIiwidXNlR2F1Z2UiLCJ1c2VUaW1lciIsInVzZVNldCJdLCJzb3VyY2VzIjpbInN0YXRzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtcbiAgY3JlYXRlQ29udGV4dCxcbiAgdXNlQ2FsbGJhY2ssXG4gIHVzZUNvbnRleHQsXG4gIHVzZUVmZmVjdCxcbiAgdXNlTWVtbyxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzYXZlQ3VycmVudFByb2plY3RDb25maWcgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5cbmV4cG9ydCB0eXBlIFN0YXRzU3RvcmUgPSB7XG4gIGluY3JlbWVudChuYW1lOiBzdHJpbmcsIHZhbHVlPzogbnVtYmVyKTogdm9pZFxuICBzZXQobmFtZTogc3RyaW5nLCB2YWx1ZTogbnVtYmVyKTogdm9pZFxuICBvYnNlcnZlKG5hbWU6IHN0cmluZywgdmFsdWU6IG51bWJlcik6IHZvaWRcbiAgYWRkKG5hbWU6IHN0cmluZywgdmFsdWU6IHN0cmluZyk6IHZvaWRcbiAgZ2V0QWxsKCk6IFJlY29yZDxzdHJpbmcsIG51bWJlcj5cbn1cblxuZnVuY3Rpb24gcGVyY2VudGlsZShzb3J0ZWQ6IG51bWJlcltdLCBwOiBudW1iZXIpOiBudW1iZXIge1xuICBjb25zdCBpbmRleCA9IChwIC8gMTAwKSAqIChzb3J0ZWQubGVuZ3RoIC0gMSlcbiAgY29uc3QgbG93ZXIgPSBNYXRoLmZsb29yKGluZGV4KVxuICBjb25zdCB1cHBlciA9IE1hdGguY2VpbChpbmRleClcbiAgaWYgKGxvd2VyID09PSB1cHBlcikge1xuICAgIHJldHVybiBzb3J0ZWRbbG93ZXJdIVxuICB9XG4gIHJldHVybiBzb3J0ZWRbbG93ZXJdISArIChzb3J0ZWRbdXBwZXJdISAtIHNvcnRlZFtsb3dlcl0hKSAqIChpbmRleCAtIGxvd2VyKVxufVxuXG5jb25zdCBSRVNFUlZPSVJfU0laRSA9IDEwMjRcblxudHlwZSBIaXN0b2dyYW0gPSB7XG4gIHJlc2Vydm9pcjogbnVtYmVyW11cbiAgY291bnQ6IG51bWJlclxuICBzdW06IG51bWJlclxuICBtaW46IG51bWJlclxuICBtYXg6IG51bWJlclxufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlU3RhdHNTdG9yZSgpOiBTdGF0c1N0b3JlIHtcbiAgY29uc3QgbWV0cmljcyA9IG5ldyBNYXA8c3RyaW5nLCBudW1iZXI+KClcbiAgY29uc3QgaGlzdG9ncmFtcyA9IG5ldyBNYXA8c3RyaW5nLCBIaXN0b2dyYW0+KClcbiAgY29uc3Qgc2V0cyA9IG5ldyBNYXA8c3RyaW5nLCBTZXQ8c3RyaW5nPj4oKVxuXG4gIHJldHVybiB7XG4gICAgaW5jcmVtZW50KG5hbWU6IHN0cmluZywgdmFsdWUgPSAxKSB7XG4gICAgICBtZXRyaWNzLnNldChuYW1lLCAobWV0cmljcy5nZXQobmFtZSkgPz8gMCkgKyB2YWx1ZSlcbiAgICB9LFxuICAgIHNldChuYW1lOiBzdHJpbmcsIHZhbHVlOiBudW1iZXIpIHtcbiAgICAgIG1ldHJpY3Muc2V0KG5hbWUsIHZhbHVlKVxuICAgIH0sXG4gICAgb2JzZXJ2ZShuYW1lOiBzdHJpbmcsIHZhbHVlOiBudW1iZXIpIHtcbiAgICAgIGxldCBoID0gaGlzdG9ncmFtcy5nZXQobmFtZSlcbiAgICAgIGlmICghaCkge1xuICAgICAgICBoID0geyByZXNlcnZvaXI6IFtdLCBjb3VudDogMCwgc3VtOiAwLCBtaW46IHZhbHVlLCBtYXg6IHZhbHVlIH1cbiAgICAgICAgaGlzdG9ncmFtcy5zZXQobmFtZSwgaClcbiAgICAgIH1cbiAgICAgIGguY291bnQrK1xuICAgICAgaC5zdW0gKz0gdmFsdWVcbiAgICAgIGlmICh2YWx1ZSA8IGgubWluKSB7XG4gICAgICAgIGgubWluID0gdmFsdWVcbiAgICAgIH1cbiAgICAgIGlmICh2YWx1ZSA+IGgubWF4KSB7XG4gICAgICAgIGgubWF4ID0gdmFsdWVcbiAgICAgIH1cbiAgICAgIC8vIFJlc2Vydm9pciBzYW1wbGluZyAoQWxnb3JpdGhtIFIpXG4gICAgICBpZiAoaC5yZXNlcnZvaXIubGVuZ3RoIDwgUkVTRVJWT0lSX1NJWkUpIHtcbiAgICAgICAgaC5yZXNlcnZvaXIucHVzaCh2YWx1ZSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGogPSBNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkgKiBoLmNvdW50KVxuICAgICAgICBpZiAoaiA8IFJFU0VSVk9JUl9TSVpFKSB7XG4gICAgICAgICAgaC5yZXNlcnZvaXJbal0gPSB2YWx1ZVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcbiAgICBhZGQobmFtZTogc3RyaW5nLCB2YWx1ZTogc3RyaW5nKSB7XG4gICAgICBsZXQgcyA9IHNldHMuZ2V0KG5hbWUpXG4gICAgICBpZiAoIXMpIHtcbiAgICAgICAgcyA9IG5ldyBTZXQoKVxuICAgICAgICBzZXRzLnNldChuYW1lLCBzKVxuICAgICAgfVxuICAgICAgcy5hZGQodmFsdWUpXG4gICAgfSxcbiAgICBnZXRBbGwoKSB7XG4gICAgICBjb25zdCByZXN1bHQ6IFJlY29yZDxzdHJpbmcsIG51bWJlcj4gPSBPYmplY3QuZnJvbUVudHJpZXMobWV
|