@apisurf/wireui 0.1.2

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.
Files changed (38) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +157 -0
  3. package/bin/wireui.mjs +233 -0
  4. package/dist/nitro.json +17 -0
  5. package/dist/public/assets/collections-CLHcfRfi.js +1 -0
  6. package/dist/public/assets/globals-CZiNFGaK.css +1 -0
  7. package/dist/public/assets/index-D5VFd_3H.js +14 -0
  8. package/dist/public/assets/kv-table-oB5gcq0i.js +2 -0
  9. package/dist/public/assets/query-BKAhHNIi.js +19 -0
  10. package/dist/public/assets/requests._requestId-Bu7E2eoI.js +1 -0
  11. package/dist/public/assets/routes-DDY2CfzA.js +1 -0
  12. package/dist/public/assets/runs._runId-CzwtVjYq.js +1 -0
  13. package/dist/public/assets/scoped-DB8raIWx.js +1 -0
  14. package/dist/public/assets/split-Br1RX2I4.js +1 -0
  15. package/dist/public/favicon.svg +1 -0
  16. package/dist/server/_chunks/ssr-renderer.mjs +26 -0
  17. package/dist/server/_libs/@tanstack/react-router+[...].mjs +15246 -0
  18. package/dist/server/_libs/h3+rou3+srvx.mjs +2086 -0
  19. package/dist/server/_libs/hookable.mjs +41 -0
  20. package/dist/server/_libs/tanstack__history.mjs +369 -0
  21. package/dist/server/_libs/ufo.mjs +64 -0
  22. package/dist/server/_runtime.mjs +35 -0
  23. package/dist/server/_ssr/api-Dw1Zb_PK.mjs +931 -0
  24. package/dist/server/_ssr/collections-CqidTQpw.mjs +495 -0
  25. package/dist/server/_ssr/empty-plugin-adapters-CCuiUfKt.mjs +5 -0
  26. package/dist/server/_ssr/kv-table-DQUkoItO.mjs +458 -0
  27. package/dist/server/_ssr/query-D4so9xiY.mjs +416 -0
  28. package/dist/server/_ssr/requests._requestId-BFukRwqL.mjs +942 -0
  29. package/dist/server/_ssr/router-CPcgc_kh.mjs +3176 -0
  30. package/dist/server/_ssr/routes-DaV9vGHn.mjs +375 -0
  31. package/dist/server/_ssr/runs._runId-Df_LmPHa.mjs +836 -0
  32. package/dist/server/_ssr/scoped-CnQ56K8h.mjs +42 -0
  33. package/dist/server/_ssr/split-DWrMIs7K.mjs +115 -0
  34. package/dist/server/_ssr/ssr.mjs +6827 -0
  35. package/dist/server/_ssr/start-zk_ZfrWl.mjs +4 -0
  36. package/dist/server/_tanstack-start-manifest_v-CARKjHfs.mjs +50 -0
  37. package/dist/server/index.mjs +368 -0
  38. package/package.json +71 -0
@@ -0,0 +1,836 @@
1
+ import { __toESM } from "../_runtime.mjs";
2
+ import { Link, require_jsx_runtime, require_react, useNavigate } from "../_libs/@tanstack/react-router+[...].mjs";
3
+ import { Callout, Card, Dot, Empty, FilterInput, IconAlert, IconButton, IconCheckCircle, IconChevronLeft, IconChevronRight, IconCollections, IconXCircle, MethodBadge, Pill, Route, SectionTitle, Segmented, Stat, StatGrid, StatusBadge, TabList, css, cx, formatBytes, formatCount, formatDateTime, formatMs, formatOffset, isFailure, mono, pagePad, plural, runTone, runVerdict, scrollPane, statusTone, tabKey, table, toneText, truncate, useNow, useTab, useUnreadable } from "./router-CPcgc_kh.mjs";
4
+ import { Code, KvTable } from "./kv-table-DQUkoItO.mjs";
5
+ //#region node_modules/.nitro/vite/services/ssr/assets/runs._runId-Df_LmPHa.js
6
+ var import_react = /* @__PURE__ */ __toESM(require_react());
7
+ var import_jsx_runtime = require_jsx_runtime();
8
+ function RunPage() {
9
+ const { run, tags, requests, checks, analytics, neighbours } = Route.useLoaderData();
10
+ const { tab = "requests" } = Route.useSearch();
11
+ const navigate = useNavigate({ from: Route.fullPath });
12
+ const unreadable = useUnreadable();
13
+ const tone = runTone(run);
14
+ useTab({
15
+ key: tabKey.run(run.id),
16
+ title: `${run.file} #${run.id}`,
17
+ icon: "run",
18
+ failed: tone === "bad"
19
+ });
20
+ if (unreadable) return unreadable;
21
+ const failedChecks = checks.filter((c) => c.status === "fail").length;
22
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
23
+ className: css({
24
+ display: "flex",
25
+ flexDir: "column",
26
+ h: "full",
27
+ minH: "0"
28
+ }),
29
+ children: [
30
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunHeader, {
31
+ run,
32
+ neighbours
33
+ }),
34
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabList, {
35
+ items: [
36
+ {
37
+ id: "requests",
38
+ label: "Requests",
39
+ count: requests.length
40
+ },
41
+ {
42
+ id: "checks",
43
+ label: "Checks",
44
+ count: checks.length ? `${checks.length - failedChecks}/${checks.length}` : 0,
45
+ tone: failedChecks ? "bad" : checks.length ? "ok" : void 0
46
+ },
47
+ {
48
+ id: "insights",
49
+ label: "Insights"
50
+ },
51
+ {
52
+ id: "details",
53
+ label: "Details",
54
+ count: tags.length || void 0
55
+ }
56
+ ],
57
+ value: tab,
58
+ onSelect: (next) => void navigate({ search: { tab: next === "requests" ? void 0 : next } })
59
+ }),
60
+ tab === "requests" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RequestsTab, {
61
+ run,
62
+ requests,
63
+ checks
64
+ }) : null,
65
+ tab === "checks" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChecksTab, {
66
+ run,
67
+ checks
68
+ }) : null,
69
+ tab === "insights" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InsightsTab, {
70
+ run,
71
+ analytics
72
+ }) : null,
73
+ tab === "details" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DetailsTab, {
74
+ run,
75
+ tags
76
+ }) : null
77
+ ]
78
+ });
79
+ }
80
+ function RunHeader({ run, neighbours }) {
81
+ const navigate = useNavigate();
82
+ const now = useNow(Date.now(), 6e4);
83
+ const tone = runTone(run);
84
+ const wall = run.finished_at ? run.finished_at - run.started_at : null;
85
+ const httpCount = run.request_count - run.connection_count;
86
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
87
+ className: css({
88
+ px: "5",
89
+ pt: "4",
90
+ pb: "3",
91
+ display: "flex",
92
+ flexDir: "column",
93
+ gap: "3",
94
+ flexShrink: 0
95
+ }),
96
+ children: [
97
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
98
+ className: css({
99
+ display: "flex",
100
+ alignItems: "flex-start",
101
+ gap: "3"
102
+ }),
103
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
104
+ className: css({
105
+ minW: "0",
106
+ flex: "1"
107
+ }),
108
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
109
+ className: css({
110
+ display: "flex",
111
+ alignItems: "center",
112
+ gap: "2"
113
+ }),
114
+ children: [
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dot, { tone }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
117
+ className: cx(truncate, css({
118
+ fontSize: "18px",
119
+ fontWeight: "600",
120
+ m: "0"
121
+ })),
122
+ children: run.file
123
+ }),
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
125
+ className: cx(mono, css({
126
+ color: "faint",
127
+ fontSize: "13px"
128
+ })),
129
+ children: ["#", run.id]
130
+ }),
131
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
132
+ tone,
133
+ children: runVerdict(run)
134
+ }),
135
+ run.env ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, { children: run.env }) : null
136
+ ]
137
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
138
+ className: css({
139
+ display: "flex",
140
+ gap: "2",
141
+ mt: "1",
142
+ color: "muted",
143
+ flexWrap: "wrap",
144
+ fontSize: "12px"
145
+ }),
146
+ children: [
147
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Link, {
148
+ to: "/collections",
149
+ search: { entry: run.entry },
150
+ className: cx(mono, css({
151
+ color: "muted",
152
+ display: "inline-flex",
153
+ gap: "1",
154
+ alignItems: "center",
155
+ _hover: { color: "accent" }
156
+ })),
157
+ title: run.entry,
158
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconCollections, { size: 12 }), run.rel]
159
+ }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: formatDateTime(now, run.started_at) }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }),
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: ["node ", run.node_version] }),
164
+ run.label && run.label !== run.file ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "·" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: run.label })] }) : null
165
+ ]
166
+ })]
167
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
168
+ className: css({
169
+ display: "flex",
170
+ gap: "1",
171
+ flexShrink: 0
172
+ }),
173
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconButton, {
174
+ label: "Older run of this script",
175
+ disabled: neighbours.prev == null,
176
+ onClick: () => neighbours.prev && void navigate({
177
+ to: "/runs/$runId",
178
+ params: { runId: String(neighbours.prev) }
179
+ }),
180
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconChevronLeft, {})
181
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconButton, {
182
+ label: "Newer run of this script",
183
+ disabled: neighbours.next == null,
184
+ onClick: () => neighbours.next && void navigate({
185
+ to: "/runs/$runId",
186
+ params: { runId: String(neighbours.next) }
187
+ }),
188
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconChevronRight, {})
189
+ })]
190
+ })]
191
+ }),
192
+ run.exit_status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Callout, {
193
+ tone: "bad",
194
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconAlert, { size: 15 }),
195
+ title: run.error_name ?? "The script threw",
196
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
197
+ className: mono,
198
+ children: run.error_message
199
+ })
200
+ }) : null,
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatGrid, { children: [
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
203
+ label: "Wall time",
204
+ value: formatMs(wall),
205
+ sub: run.finished_at ? "start to finish" : "never finished"
206
+ }),
207
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
208
+ label: "Requests",
209
+ value: formatCount(httpCount),
210
+ sub: run.connection_count ? `+ ${plural(run.connection_count, "connection")}` : `${formatMs(run.total_duration_ms)} in flight`
211
+ }),
212
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
213
+ label: "Failed requests",
214
+ value: formatCount(run.error_count),
215
+ tone: run.error_count ? "warn" : "ok",
216
+ sub: "errors and ≥ 400"
217
+ }),
218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
219
+ label: "Checks",
220
+ value: run.check_count ? `${run.check_count - run.failed_check_count}/${run.check_count}` : "—",
221
+ tone: run.failed_check_count ? "bad" : run.check_count ? "ok" : void 0,
222
+ sub: run.check_count ? run.failed_check_count ? `${run.failed_check_count} failed` : "all passed" : "none recorded"
223
+ }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
225
+ label: "Data",
226
+ value: formatBytes(run.total_bytes),
227
+ sub: "sent + received"
228
+ })
229
+ ] })
230
+ ]
231
+ });
232
+ }
233
+ var BAR = {
234
+ ok: css({ bg: "ok" }),
235
+ info: css({ bg: "info" }),
236
+ warn: css({ bg: "warn" }),
237
+ bad: css({ bg: "bad" }),
238
+ neutral: css({ bg: "faint" })
239
+ };
240
+ function RequestsTab({ run, requests, checks }) {
241
+ const navigate = useNavigate();
242
+ const [filter, setFilter] = import_react.useState("");
243
+ const [show, setShow] = import_react.useState("all");
244
+ const [withChecks, setWithChecks] = import_react.useState(true);
245
+ const needle = filter.trim().toLowerCase();
246
+ const visible = requests.filter((r) => (show === "all" || isFailure(r)) && (!needle || `${r.method} ${r.host}${r.path} ${r.status ?? ""}`.toLowerCase().includes(needle)));
247
+ const rows = import_react.useMemo(() => {
248
+ const merged = visible.map((row) => ({
249
+ type: "request",
250
+ row
251
+ }));
252
+ if (!withChecks || needle || show === "failed") return merged;
253
+ for (const check of checks) merged.push({
254
+ type: "check",
255
+ row: check
256
+ });
257
+ return merged.toSorted((a, b) => a.row.ts - b.row.ts);
258
+ }, [
259
+ visible,
260
+ checks,
261
+ withChecks,
262
+ needle,
263
+ show
264
+ ]);
265
+ const start = run.started_at;
266
+ const span = Math.max(run.finished_at ?? 0, ...requests.map((r) => r.ts + r.duration_ms), ...checks.map((c) => c.ts), start + 1) - start;
267
+ const failures = requests.filter(isFailure).length;
268
+ if (requests.length === 0 && checks.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, { title: "This run made no requests" });
269
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
270
+ className: css({
271
+ display: "flex",
272
+ flexDir: "column",
273
+ flex: "1",
274
+ minH: "0"
275
+ }),
276
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
277
+ className: css({
278
+ display: "flex",
279
+ gap: "2",
280
+ px: "5",
281
+ py: "2",
282
+ alignItems: "center",
283
+ flexShrink: 0,
284
+ flexWrap: "wrap"
285
+ }),
286
+ children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FilterInput, {
288
+ className: css({ w: "280px" }),
289
+ placeholder: "Filter by method, path or status",
290
+ value: filter,
291
+ onChange: (e) => setFilter(e.target.value)
292
+ }),
293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Segmented, {
294
+ items: [{
295
+ id: "all",
296
+ label: `All ${requests.length}`
297
+ }, {
298
+ id: "failed",
299
+ label: `Failed ${failures}`
300
+ }],
301
+ value: show,
302
+ onSelect: setShow
303
+ }),
304
+ checks.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
305
+ className: css({
306
+ display: "inline-flex",
307
+ alignItems: "center",
308
+ gap: "1.5",
309
+ color: "muted",
310
+ fontSize: "12px",
311
+ cursor: "pointer"
312
+ }),
313
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
314
+ type: "checkbox",
315
+ checked: withChecks,
316
+ onChange: (e) => setWithChecks(e.target.checked)
317
+ }), "Show checks inline"]
318
+ }) : null,
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
320
+ className: css({
321
+ ml: "auto",
322
+ color: "faint",
323
+ fontSize: "12px"
324
+ }),
325
+ children: ["Timeline spans ", formatMs(span)]
326
+ })
327
+ ]
328
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
329
+ className: scrollPane,
330
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
331
+ className: table,
332
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
334
+ className: "num",
335
+ style: { width: 40 },
336
+ children: "#"
337
+ }),
338
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "Request" }),
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "Status" }),
340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
341
+ className: "num",
342
+ children: "Time"
343
+ }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
345
+ className: "num",
346
+ children: "Size"
347
+ }),
348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
349
+ style: { width: "32%" },
350
+ children: "Timeline"
351
+ })
352
+ ] }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tbody", { children: [rows.map((item) => item.type === "check" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckLine, {
353
+ check: item.row,
354
+ start,
355
+ span
356
+ }, `c${item.row.id}`) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
357
+ "data-link": true,
358
+ onClick: () => void navigate({
359
+ to: "/requests/$requestId",
360
+ params: { requestId: String(item.row.id) }
361
+ }),
362
+ children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
364
+ className: cx("num", mono, css({ color: "faint" })),
365
+ children: item.row.seq + 1
366
+ }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
368
+ className: css({
369
+ maxW: "0",
370
+ w: "full"
371
+ }),
372
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
373
+ className: css({
374
+ display: "flex",
375
+ alignItems: "center",
376
+ gap: "2",
377
+ minW: "0"
378
+ }),
379
+ children: [
380
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodBadge, {
381
+ method: item.row.method,
382
+ kind: item.row.kind,
383
+ width: "list"
384
+ }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
386
+ className: cx(truncate, mono),
387
+ title: `${item.row.host}${item.row.path}`,
388
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
389
+ className: css({ color: "faint" }),
390
+ children: item.row.host
391
+ }), item.row.path]
392
+ }),
393
+ item.row.kind !== "http" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
394
+ className: css({
395
+ color: "faint",
396
+ fontSize: "11px",
397
+ whiteSpace: "nowrap"
398
+ }),
399
+ children: plural(item.row.message_count, "message")
400
+ }) : null
401
+ ]
402
+ })
403
+ }),
404
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, {
405
+ status: item.row.status,
406
+ error: item.row.error_name
407
+ }) }),
408
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
409
+ className: cx("num", mono),
410
+ children: formatMs(item.row.duration_ms)
411
+ }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
413
+ className: cx("num", mono, css({ color: "muted" })),
414
+ children: formatBytes(item.row.response_bytes)
415
+ }),
416
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
417
+ className: css({
418
+ position: "relative",
419
+ h: "8px"
420
+ }),
421
+ title: `${formatOffset(item.row.ts - start)} · ${formatMs(item.row.duration_ms)}`,
422
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
423
+ className: cx(css({
424
+ position: "absolute",
425
+ top: "0",
426
+ bottom: "0",
427
+ rounded: "full",
428
+ minW: "2px"
429
+ }), BAR[statusTone(item.row.status, item.row.error_name)]),
430
+ style: {
431
+ left: `${(item.row.ts - start) / span * 100}%`,
432
+ width: `${item.row.duration_ms / span * 100}%`,
433
+ opacity: item.row.kind === "http" ? 1 : .5
434
+ }
435
+ })
436
+ }) })
437
+ ]
438
+ }, item.row.id)), rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
439
+ colSpan: 6,
440
+ className: css({
441
+ textAlign: "center",
442
+ color: "faint",
443
+ py: "6"
444
+ }),
445
+ children: "No requests match."
446
+ }) }) : null] })]
447
+ })
448
+ })]
449
+ });
450
+ }
451
+ function CheckLine({ check, start, span }) {
452
+ const pass = check.status === "pass";
453
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
454
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {}),
455
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
456
+ colSpan: 4,
457
+ className: css({ maxW: "0" }),
458
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
459
+ className: css({
460
+ display: "flex",
461
+ alignItems: "center",
462
+ gap: "2",
463
+ minW: "0",
464
+ fontSize: "12px"
465
+ }),
466
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
467
+ className: cx(css({
468
+ display: "inline-flex",
469
+ w: "46px",
470
+ justifyContent: "center"
471
+ }), toneText(pass ? "ok" : "bad")),
472
+ children: pass ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconCheckCircle, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconXCircle, { size: 14 })
473
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
474
+ className: cx(truncate, pass ? css({ color: "muted" }) : css({
475
+ color: "bad",
476
+ fontWeight: "500"
477
+ })),
478
+ children: [check.name, !pass && check.message ? ` — ${check.message}` : ""]
479
+ })]
480
+ })
481
+ }),
482
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
483
+ className: css({
484
+ position: "relative",
485
+ h: "8px"
486
+ }),
487
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
488
+ className: cx(css({
489
+ position: "absolute",
490
+ top: "-1px",
491
+ w: "2px",
492
+ h: "10px",
493
+ rounded: "full"
494
+ }), pass ? BAR.ok : BAR.bad),
495
+ style: { left: `${(check.ts - start) / span * 100}%` }
496
+ })
497
+ }) })
498
+ ] });
499
+ }
500
+ function ChecksTab({ run, checks }) {
501
+ if (checks.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Empty, {
502
+ title: "No checks in this run",
503
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconCheckCircle, { size: 22 }),
504
+ children: [
505
+ "A script records expectations with ",
506
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "check()" }),
507
+ " and ",
508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "assert()" }),
509
+ " from",
510
+ " ",
511
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { children: "@apisurf/wire/kit" }),
512
+ "."
513
+ ]
514
+ });
515
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
516
+ className: scrollPane,
517
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
518
+ className: pagePad,
519
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: checks.map((check) => {
520
+ const pass = check.status === "pass";
521
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
522
+ className: css({
523
+ display: "flex",
524
+ gap: "2.5",
525
+ px: "3",
526
+ py: "2",
527
+ borderBottom: "1px solid",
528
+ borderColor: "line",
529
+ _last: { borderBottom: "none" }
530
+ }),
531
+ children: [
532
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
533
+ className: cx(css({ pt: "1px" }), toneText(pass ? "ok" : "bad")),
534
+ children: pass ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconCheckCircle, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconXCircle, { size: 16 })
535
+ }),
536
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
537
+ className: css({
538
+ flex: "1",
539
+ minW: "0"
540
+ }),
541
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
542
+ className: css({
543
+ display: "flex",
544
+ gap: "2",
545
+ alignItems: "center"
546
+ }),
547
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
548
+ className: css({ fontWeight: "500" }),
549
+ children: check.name
550
+ }), check.mode === "assert" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Pill, {
551
+ tone: pass ? "neutral" : "bad",
552
+ children: "assert"
553
+ }) : null]
554
+ }), !pass && check.message ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
555
+ className: cx(mono, css({
556
+ color: "bad",
557
+ mt: "0.5",
558
+ wordBreak: "break-word"
559
+ })),
560
+ children: check.message
561
+ }) : null]
562
+ }),
563
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
564
+ className: cx(mono, css({
565
+ color: "faint",
566
+ whiteSpace: "nowrap"
567
+ })),
568
+ children: formatOffset(check.ts - run.started_at)
569
+ })
570
+ ]
571
+ }, check.id);
572
+ }) })
573
+ })
574
+ });
575
+ }
576
+ var MIX_TONE = {
577
+ "1xx": "info",
578
+ "2xx": "ok",
579
+ "3xx": "info",
580
+ "4xx": "warn",
581
+ "5xx": "bad",
582
+ error: "bad"
583
+ };
584
+ function InsightsTab({ run, analytics }) {
585
+ const navigate = useNavigate();
586
+ if (analytics.total === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Empty, { title: "Nothing to analyse — this run made no requests" });
587
+ const maxHost = Math.max(...analytics.byHost.map((h) => h.count), 1);
588
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
589
+ className: scrollPane,
590
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
591
+ className: pagePad,
592
+ children: [
593
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(StatGrid, { children: [
594
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
595
+ label: "p50 latency",
596
+ value: formatMs(analytics.p50),
597
+ sub: `over ${plural(analytics.latencySamples, "HTTP request")}`
598
+ }),
599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
600
+ label: "p95 latency",
601
+ value: formatMs(analytics.p95)
602
+ }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
604
+ label: "p99 latency",
605
+ value: formatMs(analytics.p99)
606
+ }),
607
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Stat, {
608
+ label: "Hosts",
609
+ value: formatCount(analytics.byHost.length),
610
+ sub: analytics.byHost[0]?.host
611
+ })
612
+ ] }),
613
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "Status mix" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Card, {
614
+ className: css({ p: "3" }),
615
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
616
+ className: css({
617
+ display: "flex",
618
+ gap: "2px",
619
+ h: "14px"
620
+ }),
621
+ children: analytics.statusMix.map((mix) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
622
+ title: `${mix.status_class}: ${mix.count}`,
623
+ className: cx(css({
624
+ rounded: "sm",
625
+ minW: "4px"
626
+ }), BAR[MIX_TONE[mix.status_class] ?? "neutral"]),
627
+ style: { flexGrow: mix.count }
628
+ }, mix.status_class))
629
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
630
+ className: css({
631
+ display: "flex",
632
+ gap: "4",
633
+ mt: "2.5",
634
+ flexWrap: "wrap"
635
+ }),
636
+ children: analytics.statusMix.map((mix) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
637
+ className: css({
638
+ display: "inline-flex",
639
+ alignItems: "center",
640
+ gap: "1.5"
641
+ }),
642
+ children: [
643
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: cx(css({
644
+ w: "8px",
645
+ h: "8px",
646
+ rounded: "xs"
647
+ }), BAR[MIX_TONE[mix.status_class] ?? "neutral"]) }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
649
+ className: mono,
650
+ children: mix.status_class
651
+ }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
653
+ className: css({ color: "muted" }),
654
+ children: [
655
+ mix.count,
656
+ " · ",
657
+ Math.round(mix.count / analytics.total * 100),
658
+ "%"
659
+ ]
660
+ })
661
+ ]
662
+ }, mix.status_class))
663
+ })]
664
+ })] }),
665
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
666
+ className: css({
667
+ display: "grid",
668
+ gridTemplateColumns: {
669
+ base: "1fr",
670
+ lg: "1fr 1fr"
671
+ },
672
+ gap: "5"
673
+ }),
674
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "By host" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", {
675
+ className: table,
676
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
677
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { children: "Host" }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
679
+ className: "num",
680
+ children: "Calls"
681
+ }),
682
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
683
+ className: "num",
684
+ children: "Errors"
685
+ }),
686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", {
687
+ className: "num",
688
+ children: "p95"
689
+ })
690
+ ] }) }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: analytics.byHost.map((host) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
692
+ className: cx(mono, truncate),
693
+ children: host.host
694
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
695
+ className: css({
696
+ h: "3px",
697
+ mt: "1",
698
+ bg: "accent",
699
+ rounded: "full",
700
+ opacity: .6
701
+ }),
702
+ style: { width: `${host.count / maxHost * 100}%` }
703
+ })] }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
705
+ className: "num",
706
+ children: host.count
707
+ }),
708
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
709
+ className: cx("num", host.errors ? css({ color: "warn" }) : void 0),
710
+ children: host.errors
711
+ }),
712
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
713
+ className: cx("num", mono),
714
+ children: formatMs(host.p95)
715
+ })
716
+ ] }, host.host)) })]
717
+ }) })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "Slowest requests" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", {
718
+ className: table,
719
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: analytics.slowest.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", {
720
+ "data-link": true,
721
+ onClick: () => void navigate({
722
+ to: "/requests/$requestId",
723
+ params: { requestId: String(row.id) }
724
+ }),
725
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
726
+ className: css({
727
+ maxW: "0",
728
+ w: "full"
729
+ }),
730
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
731
+ className: css({
732
+ display: "flex",
733
+ gap: "2",
734
+ alignItems: "center",
735
+ minW: "0"
736
+ }),
737
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MethodBadge, {
738
+ method: row.method,
739
+ width: "list"
740
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
741
+ className: cx(mono, truncate),
742
+ children: row.path
743
+ })]
744
+ })
745
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", {
746
+ className: cx("num", mono),
747
+ children: formatMs(row.duration_ms)
748
+ })]
749
+ }, row.id)) })
750
+ }) })] })]
751
+ }),
752
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", {
753
+ className: css({
754
+ color: "faint",
755
+ fontSize: "12px",
756
+ m: "0"
757
+ }),
758
+ children: ["Latency covers HTTP requests only; a WebSocket or SSE connection's duration is how long it stayed open.", run.connection_count ? ` This run opened ${plural(run.connection_count, "connection")}.` : ""]
759
+ })
760
+ ]
761
+ })
762
+ });
763
+ }
764
+ function DetailsTab({ run, tags }) {
765
+ const now = useNow(Date.now(), 6e4);
766
+ const rows = [
767
+ {
768
+ name: "id",
769
+ value: String(run.id)
770
+ },
771
+ {
772
+ name: "uid",
773
+ value: run.uid
774
+ },
775
+ {
776
+ name: "label",
777
+ value: run.label ?? "—"
778
+ },
779
+ {
780
+ name: "entry",
781
+ value: run.entry
782
+ },
783
+ {
784
+ name: "cwd",
785
+ value: run.cwd
786
+ },
787
+ {
788
+ name: "env",
789
+ value: run.env ?? "—"
790
+ },
791
+ {
792
+ name: "node",
793
+ value: run.node_version
794
+ },
795
+ {
796
+ name: "started",
797
+ value: formatDateTime(now, run.started_at)
798
+ },
799
+ {
800
+ name: "finished",
801
+ value: run.finished_at ? formatDateTime(now, run.finished_at) : "—"
802
+ },
803
+ {
804
+ name: "exit status",
805
+ value: run.exit_status ?? "—"
806
+ }
807
+ ];
808
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
809
+ className: scrollPane,
810
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
811
+ className: pagePad,
812
+ children: [
813
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "Run" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KvTable, {
814
+ rows,
815
+ empty: "",
816
+ filterable: false,
817
+ nameLabel: "Field"
818
+ }) })] }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "Tags" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(KvTable, {
820
+ rows: tags.map((t) => ({
821
+ name: t.key,
822
+ value: t.value
823
+ })),
824
+ empty: "No tags — add them with wire execute --tag key=value",
825
+ filterable: false
826
+ }) })] }),
827
+ run.entry_source ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { children: "Inline source" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Card, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
828
+ className: css({ bg: "well" }),
829
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Code, { text: run.entry_source })
830
+ }) })] }) : null
831
+ ]
832
+ })
833
+ });
834
+ }
835
+ //#endregion
836
+ export { RunPage as component };