@oh-my-pi/omp-stats 18.16.0 → 18.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/client/index.css +1 -1
- package/dist/client/index.js +102 -99
- package/dist/types/client/api.d.ts +4 -1
- package/dist/types/client/app/nav.d.ts +1 -1
- package/dist/types/client/charts/Chart.d.ts +16 -8
- package/dist/types/client/charts/DotWhisker.d.ts +50 -0
- package/dist/types/client/charts/Sparkline.d.ts +1 -1
- package/dist/types/client/charts/Waterfall.d.ts +26 -0
- package/dist/types/client/charts/index.d.ts +2 -0
- package/dist/types/client/charts/types.d.ts +8 -2
- package/dist/types/client/routes/SpotlightRoute.d.ts +7 -0
- package/dist/types/client/routes/index.d.ts +1 -0
- package/dist/types/client/spotlight/Compounding.d.ts +8 -0
- package/dist/types/client/spotlight/Coverage.d.ts +6 -0
- package/dist/types/client/spotlight/Economics.d.ts +10 -0
- package/dist/types/client/spotlight/FetchBack.d.ts +9 -0
- package/dist/types/client/spotlight/Hero.d.ts +12 -0
- package/dist/types/client/spotlight/Overhead.d.ts +6 -0
- package/dist/types/client/spotlight/Ranking.d.ts +6 -0
- package/dist/types/client/spotlight/ResultsFeed.d.ts +6 -0
- package/dist/types/client/spotlight/Savings.d.ts +10 -0
- package/dist/types/client/spotlight/ShareTrend.d.ts +8 -0
- package/dist/types/client/spotlight/Steps.d.ts +6 -0
- package/dist/types/client/spotlight/TimeCost.d.ts +6 -0
- package/dist/types/client/spotlight/chips.d.ts +16 -0
- package/dist/types/client/spotlight/meta.d.ts +64 -0
- package/dist/types/db.d.ts +26 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/parser.d.ts +4 -0
- package/dist/types/rollup.d.ts +22 -1
- package/dist/types/shared-types.d.ts +419 -0
- package/dist/types/spotlight-aggregator.d.ts +189 -0
- package/dist/types/spotlight-bootstrap.d.ts +32 -0
- package/dist/types/spotlight-ingest.d.ts +258 -0
- package/dist/types/spotlight-measures.d.ts +51 -0
- package/package.json +5 -5
- package/src/aggregator.ts +3 -1
- package/src/client/App.tsx +3 -0
- package/src/client/api.ts +21 -0
- package/src/client/app/nav.ts +4 -1
- package/src/client/charts/Chart.tsx +105 -22
- package/src/client/charts/DotWhisker.tsx +149 -0
- package/src/client/charts/Sparkline.tsx +3 -2
- package/src/client/charts/Waterfall.tsx +86 -0
- package/src/client/charts/index.ts +2 -0
- package/src/client/charts/types.ts +9 -2
- package/src/client/react-css.d.ts +1 -1
- package/src/client/routes/SpotlightRoute.tsx +138 -0
- package/src/client/routes/index.ts +1 -0
- package/src/client/spotlight/Compounding.tsx +222 -0
- package/src/client/spotlight/Coverage.tsx +96 -0
- package/src/client/spotlight/Economics.tsx +167 -0
- package/src/client/spotlight/FetchBack.tsx +222 -0
- package/src/client/spotlight/Hero.tsx +177 -0
- package/src/client/spotlight/Overhead.tsx +135 -0
- package/src/client/spotlight/Ranking.tsx +248 -0
- package/src/client/spotlight/ResultsFeed.tsx +354 -0
- package/src/client/spotlight/Savings.tsx +355 -0
- package/src/client/spotlight/ShareTrend.tsx +103 -0
- package/src/client/spotlight/Steps.tsx +225 -0
- package/src/client/spotlight/TimeCost.tsx +64 -0
- package/src/client/spotlight/chips.tsx +63 -0
- package/src/client/spotlight/meta.ts +206 -0
- package/src/client/spotlight/spotlight.css +1387 -0
- package/src/client/styles.css +289 -1
- package/src/db.ts +480 -2
- package/src/index.ts +10 -1
- package/src/live.ts +21 -9
- package/src/parser.ts +44 -8
- package/src/rollup.ts +203 -10
- package/src/server.ts +42 -0
- package/src/shared-types.ts +453 -0
- package/src/spotlight-aggregator.ts +1738 -0
- package/src/spotlight-bootstrap.ts +110 -0
- package/src/spotlight-ingest.ts +981 -0
- package/src/spotlight-measures.ts +667 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seeded session-clustered bootstrap for the Spotlight dashboard's estimates: 1000 resamples (seeded per key, so
|
|
3
|
+
* every request returns the same interval), 5th and 95th percentiles, no interval under 3 sessions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { SpotlightEstimate } from "./shared-types";
|
|
7
|
+
|
|
8
|
+
const BOOTSTRAP_RESAMPLES = 1000;
|
|
9
|
+
/** Fewest sessions an interval is drawn from. */
|
|
10
|
+
export const BOOTSTRAP_MIN_SESSIONS = 3;
|
|
11
|
+
|
|
12
|
+
/** mulberry32: a small seeded PRNG, so intervals are identical across requests. */
|
|
13
|
+
function seededRandom(seed: number): () => number {
|
|
14
|
+
let state = seed >>> 0;
|
|
15
|
+
return () => {
|
|
16
|
+
state = (state + 0x6d2b79f5) >>> 0;
|
|
17
|
+
let t = state;
|
|
18
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
19
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
20
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Linear-interpolated quantile `p` of ascending `sorted` (non-empty). */
|
|
25
|
+
export function quantile(sorted: ArrayLike<number>, p: number): number {
|
|
26
|
+
const at = (sorted.length - 1) * p;
|
|
27
|
+
const lo = Math.floor(at);
|
|
28
|
+
const hi = Math.ceil(at);
|
|
29
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (at - lo);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The 90% interval of a statistic over seeded resamples: `resample` draws its own resample of the sessions with
|
|
34
|
+
* `random` (uniform in [0, 1)) and returns the statistic, or null when the resample cannot define it.
|
|
35
|
+
*/
|
|
36
|
+
export function bootstrapInterval(
|
|
37
|
+
key: string,
|
|
38
|
+
resample: (random: () => number) => number | null,
|
|
39
|
+
): Pick<SpotlightEstimate, "lo" | "hi"> {
|
|
40
|
+
const random = seededRandom(Bun.hash.crc32(key));
|
|
41
|
+
const samples: number[] = [];
|
|
42
|
+
for (let r = 0; r < BOOTSTRAP_RESAMPLES; r++) {
|
|
43
|
+
const sample = resample(random);
|
|
44
|
+
if (sample !== null && Number.isFinite(sample)) samples.push(sample);
|
|
45
|
+
}
|
|
46
|
+
return percentileInterval(samples);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** 5th and 95th percentiles of bootstrap samples (sorted in place); null without any. */
|
|
50
|
+
export function percentileInterval(samples: number[] | Float64Array): Pick<SpotlightEstimate, "lo" | "hi"> {
|
|
51
|
+
if (samples.length === 0) return { lo: null, hi: null };
|
|
52
|
+
samples.sort((a, b) => a - b);
|
|
53
|
+
return { lo: quantile(samples, 0.05), hi: quantile(samples, 0.95) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A joint bootstrap: one array of {@link BOOTSTRAP_RESAMPLES} seeded resample rounds that several statistics share.
|
|
58
|
+
* `round` draws one resample with `random` and stores each statistic of it at index `r` of that statistic's array
|
|
59
|
+
* (made by {@link jointSamples}); {@link percentileInterval} then turns every array into its interval. One pass
|
|
60
|
+
* replaces a {@link bootstrapInterval} per statistic.
|
|
61
|
+
*/
|
|
62
|
+
export function bootstrapRounds(key: string, round: (random: () => number, r: number) => void): void {
|
|
63
|
+
const random = seededRandom(Bun.hash.crc32(key));
|
|
64
|
+
for (let r = 0; r < BOOTSTRAP_RESAMPLES; r++) round(random, r);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** One statistic's samples for {@link bootstrapRounds}. */
|
|
68
|
+
export function jointSamples(): Float64Array {
|
|
69
|
+
return new Float64Array(BOOTSTRAP_RESAMPLES);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The means of seeded resamples of `values` (one per bootstrap round, in round order), so statistics of several
|
|
74
|
+
* groups can be combined round by round.
|
|
75
|
+
*/
|
|
76
|
+
export function resampledMeans(values: readonly number[], key: string): Float64Array {
|
|
77
|
+
const random = seededRandom(Bun.hash.crc32(key));
|
|
78
|
+
const means = new Float64Array(BOOTSTRAP_RESAMPLES);
|
|
79
|
+
for (let r = 0; r < BOOTSTRAP_RESAMPLES; r++) {
|
|
80
|
+
let sum = 0;
|
|
81
|
+
for (let i = 0; i < values.length; i++) sum += values[Math.floor(random() * values.length)];
|
|
82
|
+
means[r] = sum / values.length;
|
|
83
|
+
}
|
|
84
|
+
return means;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Ratio of sums over sessions (`[numerator, denominator]` per session) with its bootstrap interval. */
|
|
88
|
+
export function bootstrapRatio(pairs: readonly (readonly [number, number])[], key: string): SpotlightEstimate {
|
|
89
|
+
let num = 0;
|
|
90
|
+
let den = 0;
|
|
91
|
+
for (const [a, b] of pairs) {
|
|
92
|
+
num += a;
|
|
93
|
+
den += b;
|
|
94
|
+
}
|
|
95
|
+
const value = pairs.length > 0 && den > 0 ? num / den : null;
|
|
96
|
+
if (value === null || pairs.length < BOOTSTRAP_MIN_SESSIONS) return { value, lo: null, hi: null };
|
|
97
|
+
return {
|
|
98
|
+
value,
|
|
99
|
+
...bootstrapInterval(key, random => {
|
|
100
|
+
let sampleNum = 0;
|
|
101
|
+
let sampleDen = 0;
|
|
102
|
+
for (let i = 0; i < pairs.length; i++) {
|
|
103
|
+
const [a, b] = pairs[Math.floor(random() * pairs.length)];
|
|
104
|
+
sampleNum += a;
|
|
105
|
+
sampleDen += b;
|
|
106
|
+
}
|
|
107
|
+
return sampleDen > 0 ? sampleNum / sampleDen : null;
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|