Public: anyone can read this; the crew can change it.
The crew teams page's Server-Timing names its phases: session, each read, render #3435
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/3404-the-crew-teams-page-s-server-timing-name → main 1 commit fork
2 files
changed +55
−5
src/web/pages/teams.ts +12 −5
| @@ -7,6 +7,7 @@ | ||
| 7 | 7 | import { changeTeam, readTeams, teamChanges, TEAM_SAYS } from "../../actions/teams.ts"; |
| 8 | 8 | import { CHARTER_MAX, DIAL_MAX } from "../../core/teams.ts"; |
| 9 | 9 | import type { Ctx } from "../../http/context.ts"; |
| 10 | import { notePhase, takeSessionTime, timePhase } from "../../core/route_timing.ts"; | |
| 10 | 11 | import { notFoundPage, settingsLayout } from "../chrome.ts"; |
| 11 | 12 | import { field, readForm } from "../form.ts"; |
| 12 | 13 | import { eventStream } from "../../http/sse.ts"; |
| @@ -19,11 +20,13 @@ const isAdminPerson = (ctx: Ctx): boolean => | ||
| 19 | 20 | ctx.auth.user?.isAdmin === true && ctx.auth.user.kind !== "agent"; |
| 20 | 21 | |
| 21 | 22 | /** The page's three regions, as HTML text: what the first paint and the stream both send. */ |
| 22 | function regions(ctx: Ctx): Record<string, string> | undefined { | |
| 23 | const read = readTeams(ctx.app, ctx.auth.user); | |
| 23 | function regions(ctx: Ctx, timed = false): Record<string, string> | undefined { | |
| 24 | const phase = <T>(name: string, work: () => T): T => | |
| 25 | timed ? timePhase(ctx.request, name, work) : work(); | |
| 26 | const read = phase("teams", () => readTeams(ctx.app, ctx.auth.user)); | |
| 24 | 27 | if (!read.ok) return undefined; |
| 25 | 28 | const { teams, agents } = read.value; |
| 26 | const changes = teamChanges(ctx.app, 10); | |
| 29 | const changes = phase("changes", () => teamChanges(ctx.app, 10)); | |
| 27 | 30 | const total = teams.reduce((n, t) => n + t.dial, 0); |
| 28 | 31 | const cards = teams.map((t) => |
| 29 | 32 | card( |
| @@ -113,10 +116,12 @@ export function teamsStream(ctx: Ctx): Response { | ||
| 113 | 116 | |
| 114 | 117 | // GET /settings/teams |
| 115 | 118 | export function teamsPage(ctx: Ctx): Response { |
| 116 | const r = regions(ctx); | |
| 119 | notePhase(ctx.request, "session", takeSessionTime(ctx.request)); | |
| 120 | const r = regions(ctx, true); | |
| 117 | 121 | if (!r) return notFoundPage(ctx); |
| 122 | const rendering = performance.now(); | |
| 118 | 123 | const at = (id: string): Html => raw(r[id]!); |
| 119 | return page({ | |
| 124 | const out = page({ | |
| 120 | 125 | ctx, |
| 121 | 126 | title: "Teams", |
| 122 | 127 | scriptJs: teamsLiveJs, |
| @@ -134,6 +139,8 @@ export function teamsPage(ctx: Ctx): Response { | ||
| 134 | 139 | "Account settings", |
| 135 | 140 | )}`, |
| 136 | 141 | }); |
| 142 | notePhase(ctx.request, "render", performance.now() - rendering); | |
| 143 | return out; | |
| 137 | 144 | } |
| 138 | 145 | |
| 139 | 146 | // POST /settings/teams/:slug |
test/teams-timing.test.ts added +43
| @@ -0,0 +1,43 @@ | ||
| 1 | /** /settings/teams names its phases in Server-Timing. */ | |
| 2 | import { afterAll, beforeAll, expect, test } from "bun:test"; | |
| 3 | import { mkdtemp, rm } from "node:fs/promises"; | |
| 4 | import { tmpdir } from "node:os"; | |
| 5 | import { join } from "node:path"; | |
| 6 | import { createApp, type App } from "../src/app.ts"; | |
| 7 | import { serve, type Servers } from "../src/http/server.ts"; | |
| 8 | import { seedAdmin } from "./helpers/admin.ts"; | |
| 9 | ||
| 10 | let dataDir = ""; | |
| 11 | let app: App, servers: Servers, bearer = ""; | |
| 12 | ||
| 13 | beforeAll(async () => { | |
| 14 | dataDir = await mkdtemp(join(tmpdir(), "forge-ttime-")); | |
| 15 | app = createApp({ dataDir, port: 0 }); | |
| 16 | servers = await serve(app); | |
| 17 | await seedAdmin(app, { username: "alice", email: "[email protected]", password: "correct-horse-battery" }); | |
| 18 | const minted = await fetch(`${servers.url}/api/v1/tokens`, { | |
| 19 | method: "POST", | |
| 20 | headers: { "content-type": "application/json" }, | |
| 21 | body: JSON.stringify({ adminConfirm: true, username: "alice", password: "correct-horse-battery", name: "cli", scope: "admin" }), | |
| 22 | }); | |
| 23 | bearer = ((await minted.json()) as { token: string }).token; | |
| 24 | }); | |
| 25 | ||
| 26 | afterAll(async () => { | |
| 27 | servers.stop(); | |
| 28 | app.close(); | |
| 29 | await rm(dataDir, { recursive: true, force: true }); | |
| 30 | }); | |
| 31 | ||
| 32 | test("the teams page names session, teams, changes and render once each", async () => { | |
| 33 | const res = await fetch(`${servers.url}/settings/teams`, { headers: { authorization: `Bearer ${bearer}` } }); | |
| 34 | await res.arrayBuffer(); | |
| 35 | expect(res.status).toBe(200); | |
| 36 | const parts = (res.headers.get("server-timing") ?? "").split(", "); | |
| 37 | const names = parts.map((p) => p.split(";")[0]!); | |
| 38 | for (const n of ["session", "teams", "changes", "render"]) { | |
| 39 | expect(names.filter((x) => x === n).length).toBe(1); | |
| 40 | const part = parts.find((p) => p.startsWith(`${n};`))!; | |
| 41 | expect(Number(/dur=([\d.]+)/.exec(part)?.[1])).not.toBeNaN(); | |
| 42 | } | |
| 43 | }); |
new above
offline