cloudflare/vinext

Public

mirrored from https://github.com/cloudflare/vinextAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.0.9

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

examples/benchmarks/app/components/commit-detail.tsx

236lines · modecode

1"use client";
2
3import { useState, useEffect } from "react";
4import { Badge } from "@cloudflare/kumo/components/badge";
5import { Table } from "@cloudflare/kumo/components/table";
6import {
7 formatMs,
8 formatBytes,
9 speedup,
10 sizeReduction,
11 isImprovement,
12 RUNNER_LABELS,
13} from "./format";
14
15// ─── Types ───────────────────────────────────────────────────────────────────
16
17interface RunnerResult {
18 runner: string;
19 build_time_mean: number | null;
20 build_time_stddev: number | null;
21 build_time_min: number | null;
22 build_time_max: number | null;
23 bundle_size_raw: number | null;
24 bundle_size_gzip: number | null;
25 bundle_file_count: number | null;
26 dev_cold_start_ms: number | null;
27 dev_peak_rss_kb: number | null;
28 platform: string | null;
29 arch: string | null;
30 node_version: string | null;
31 cpu_count: number | null;
32 run_count: number | null;
33 commit_message: string;
34 commit_date: string;
35 run_date: string;
36}
37
38// ─── Component ───────────────────────────────────────────────────────────────
39
40export function CommitDetail({ sha }: { sha: string }) {
41 const [data, setData] = useState<RunnerResult[]>([]);
42 const [loading, setLoading] = useState(true);
43 const [error, setError] = useState<string | null>(null);
44
45 useEffect(() => {
46 fetch(`/api/commit/${sha}`)
47 .then((res) => {
48 if (!res.ok) throw new Error(res.status === 404 ? "Commit not found" : `HTTP ${res.status}`);
49 return res.json();
50 })
51 .then((d) => {
52 setData(d);
53 setLoading(false);
54 })
55 .catch((err) => {
56 setError(err.message);
57 setLoading(false);
58 });
59 }, [sha]);
60
61 if (loading) {
62 return <div className="py-20 text-center text-gray-400">Loading...</div>;
63 }
64
65 if (error) {
66 return (
67 <div className="rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
68 {error}
69 </div>
70 );
71 }
72
73 if (data.length === 0) {
74 return <div className="py-12 text-center text-gray-400">No results for this commit.</div>;
75 }
76
77 const first = data[0];
78 const nextjs = data.find((d) => d.runner === "nextjs");
79
80 return (
81 <div className="space-y-6">
82 {/* Commit header */}
83 <div>
84 <div className="flex items-center gap-3">
85 <h1 className="text-xl font-bold font-mono">{sha}</h1>
86 <Badge variant="secondary">
87 {first.run_count} run{first.run_count !== 1 ? "s" : ""}
88 </Badge>
89 </div>
90 {first.commit_message && (
91 <p className="mt-1 text-sm text-gray-600">{first.commit_message}</p>
92 )}
93 <div className="mt-2 flex gap-4 text-xs text-gray-400">
94 <span>Committed: {new Date(first.commit_date).toLocaleString()}</span>
95 <span>Benchmarked: {new Date(first.run_date).toLocaleString()}</span>
96 {first.platform && (
97 <span>
98 {first.platform}/{first.arch} &middot; Node {first.node_version} &middot;{" "}
99 {first.cpu_count} CPUs
100 </span>
101 )}
102 </div>
103 </div>
104
105 {/* Build Time */}
106 <MetricSection title="Production Build Time">
107 <Table>
108 <Table.Header>
109 <Table.Row>
110 <Table.Head>Framework</Table.Head>
111 <Table.Head>Mean</Table.Head>
112 <Table.Head>StdDev</Table.Head>
113 <Table.Head>Min</Table.Head>
114 <Table.Head>Max</Table.Head>
115 <Table.Head>vs Next.js</Table.Head>
116 </Table.Row>
117 </Table.Header>
118 <Table.Body>
119 {data.map((r) => (
120 <Table.Row key={r.runner}>
121 <Table.Cell className="font-medium">
122 {RUNNER_LABELS[r.runner] || r.runner}
123 </Table.Cell>
124 <Table.Cell>{formatMs(r.build_time_mean)}</Table.Cell>
125 <Table.Cell className="text-gray-400">
126 {r.build_time_stddev !== null ? `\u00b1${formatMs(r.build_time_stddev)}` : "-"}
127 </Table.Cell>
128 <Table.Cell>{formatMs(r.build_time_min)}</Table.Cell>
129 <Table.Cell>{formatMs(r.build_time_max)}</Table.Cell>
130 <Table.Cell>
131 {r.runner === "nextjs" ? (
132 <span className="text-gray-400">baseline</span>
133 ) : (() => {
134 const label = speedup(nextjs?.build_time_mean ?? null, r.build_time_mean);
135 return label ? (
136 <Badge variant={isImprovement(label) ? "primary" : "destructive"}>
137 {label}
138 </Badge>
139 ) : "-";
140 })()}
141 </Table.Cell>
142 </Table.Row>
143 ))}
144 </Table.Body>
145 </Table>
146 </MetricSection>
147
148 {/* Client Bundle Size */}
149 <MetricSection title="Production Bundle Size (Client)">
150 <Table>
151 <Table.Header>
152 <Table.Row>
153 <Table.Head>Framework</Table.Head>
154 <Table.Head>Files</Table.Head>
155 <Table.Head>Raw</Table.Head>
156 <Table.Head>Gzipped</Table.Head>
157 <Table.Head>vs Next.js (gzip)</Table.Head>
158 </Table.Row>
159 </Table.Header>
160 <Table.Body>
161 {data.map((r) => (
162 <Table.Row key={r.runner}>
163 <Table.Cell className="font-medium">
164 {RUNNER_LABELS[r.runner] || r.runner}
165 </Table.Cell>
166 <Table.Cell>{r.bundle_file_count ?? "-"}</Table.Cell>
167 <Table.Cell>{formatBytes(r.bundle_size_raw)}</Table.Cell>
168 <Table.Cell>{formatBytes(r.bundle_size_gzip)}</Table.Cell>
169 <Table.Cell>
170 {r.runner === "nextjs" ? (
171 <span className="text-gray-400">baseline</span>
172 ) : (() => {
173 const label = sizeReduction(nextjs?.bundle_size_gzip ?? null, r.bundle_size_gzip);
174 return label ? (
175 <Badge variant={isImprovement(label) ? "primary" : "destructive"}>
176 {label}
177 </Badge>
178 ) : "-";
179 })()}
180 </Table.Cell>
181 </Table.Row>
182 ))}
183 </Table.Body>
184 </Table>
185 </MetricSection>
186
187 {/* Dev Cold Start */}
188 <MetricSection title="Dev Server Cold Start">
189 <Table>
190 <Table.Header>
191 <Table.Row>
192 <Table.Head>Framework</Table.Head>
193 <Table.Head>Mean Cold Start</Table.Head>
194 <Table.Head>Peak RSS</Table.Head>
195 <Table.Head>vs Next.js</Table.Head>
196 </Table.Row>
197 </Table.Header>
198 <Table.Body>
199 {data.map((r) => (
200 <Table.Row key={r.runner}>
201 <Table.Cell className="font-medium">
202 {RUNNER_LABELS[r.runner] || r.runner}
203 </Table.Cell>
204 <Table.Cell>{formatMs(r.dev_cold_start_ms)}</Table.Cell>
205 <Table.Cell>
206 {r.dev_peak_rss_kb ? `${Math.round(r.dev_peak_rss_kb / 1024)} MB` : "-"}
207 </Table.Cell>
208 <Table.Cell>
209 {r.runner === "nextjs" ? (
210 <span className="text-gray-400">baseline</span>
211 ) : (() => {
212 const label = speedup(nextjs?.dev_cold_start_ms ?? null, r.dev_cold_start_ms);
213 return label ? (
214 <Badge variant={isImprovement(label) ? "primary" : "destructive"}>
215 {label}
216 </Badge>
217 ) : "-";
218 })()}
219 </Table.Cell>
220 </Table.Row>
221 ))}
222 </Table.Body>
223 </Table>
224 </MetricSection>
225 </div>
226 );
227}
228
229function MetricSection({ title, children }: { title: string; children: React.ReactNode }) {
230 return (
231 <section>
232 <h2 className="mb-2 text-base font-semibold">{title}</h2>
233 <div className="overflow-x-auto rounded-lg border border-gray-200 bg-white">{children}</div>
234 </section>
235 );
236}
237